Арифметические операции в регулярном выражении
Я использую плагин gedit regex (регулярное выражение стиля Python). Я хотел бы сделать некоторую арифметическую операцию для обратной ссылки на группу.
Например:
PART 1 DATA MODELS Chapter
2 Entity-Relationship Model 27
Я хотел бы изменить его как
PART 1 DATA MODELS Chapter 25
2 Entity-Relationship Model 27
Мое регулярное выражение ^(PART.*)\n(.*\s(\d+))\n
, и я хотел бы заменить его чем-то вроде \1 (\3-2)\n\2\n
, где \3-2
означает обратную ссылку \3
минус 2. Но замена регулярного выражения неверна. Интересно, как это сделать? Спасибо!
Ответы
Ответ 1
Вы можете перейти к функции re.sub
lambda, которая принимает объект re.MatchObject
для каждого совпадающего совпадения шаблонов и возвращает строку замены. Например:
import re
print re.sub("(\d+)\+(\d+)",
lambda m: str(int(m.group(1))+int(m.group(2))),
"If 2+2 is 4 then 1+2+3+4 is 10")
печатает
Если 4 равно 4, то 3 + 7 составляет 10
Вы можете легко применить его к своей проблеме.
Ответ 2
Следующий код делает то, что вы хотите от строки, которую вы дали в качестве примера. Один момент заключается в том, что он очень специфичен для формата этой строки. Он не может управлять любой изменчивостью в строке. Это действительно ограничено только этим типом строкового формата.
import re
ss = '''PART 1 DATA MODELS Chapter
2 Entity-Relationship Model 27
The sun is shining
PART 1 DATA MODELS Chapter
13 Entity-Relationship Model 45
'''
regx = re.compile('^(PART.*)(\n(\d*).*\s(\d+)\n)',re.MULTILINE)
def repl(mat):
return ''.join((mat.group(1),' ',
str(int(mat.group(4))-int(mat.group(3))),
mat.group(2)))
for mat in regx.finditer(ss):
print mat.groups()
print
print regx.sub(repl,ss)
результат
('PART 1 DATA MODELS Chapter', '\n2 Entity-Relationship Model 27\n', '2', '27')
('PART 1 DATA MODELS Chapter', '\n13 Entity-Relationship Model 45\n', '13', '45')
PART 1 DATA MODELS Chapter 25
2 Entity-Relationship Model 27
The sun is shining
PART 1 DATA MODELS Chapter 32
13 Entity-Relationship Model 45
Отредактировано: я забыл флаг re.MULTILINE
Ответ 3
Я не знаю, что вы можете делать арифметические или другие вычисления в регулярных выражениях. Если там есть механизм регулярных выражений, который поддерживает это, он будет действительно изящным! Но я понимаю, что это было бы непрактично, без чрезмерного замедления двигателя регулярных выражений.
Думаю, лучше всего будет использовать функцию/метод регулярного выражения sub
:
re.sub(pattern, repl, string[, count, flags])
Вернуть строку полученных путем замены самых левых неперекрывающихся вхождений шаблон в строке с заменой repl. Если шаблон не найден, строка возвращается без изменений. repl может быть строкой или функцией; если это строка, любые обратные слэши в ней обрабатываются. То есть, \n преобразуется в один символ новой строки, \r преобразуется в linefeed и т.д. Неизвестные escape-последовательности, такие как \j, остаются в силе. Backreferences, такие как \6, заменяются подстрокой, соответствующей группа 6 в шаблоне. Например:
>>> re.sub(r'def\s+([a-zA-Z_][a-zA-Z_0-9]*)\s*\(\s*\):',
... r'static PyObject*\npy_\1(void)\n{',
... 'def myfunc():')
'static PyObject*\npy_myfunc(void)\n{'
Если repl - это функция, это для каждого неперекрывающегося появления шаблона. Функция принимает один аргумент объекта сопоставления и возвращает замену строка. Например:
>>> def dashrepl(matchobj):
... if matchobj.group(0) == '-': return ' '
... else: return '-'
>>> re.sub('-{1,2}', dashrepl, 'pro----gram-files')
'pro--gram files'
>>> re.sub(r'\sAND\s', ' & ', 'Baked Beans And Spam', flags=re.IGNORECASE)
'Baked Beans & Spam'
Шаблон может быть строкой или объектом RE.
Необязательный аргумент count - максимальное количество шаблонов замещения, подлежащие замене; count должно быть неотрицательным целым числом. Если опущено или равно нулю, все вхождения будут заменены. Пустые совпадения для шаблон заменяется только тогда, когда он не смежен с предыдущим совпадением, поэтому sub ('x *', '-', 'abc') возвращает '-a-b-c -'.
В дополнение к символьным экранам и обратным ссылкам, как описано выше, \g будет использовать подстроку, соответствующую группе с именем name, как определено синтаксисом (? P...).\g использует номер соответствующей группы; \g < 2 > , следовательно, эквивалентно \2, но не является неоднозначным в замене, например \g < 2 > 0.\20 будет интерпретируется как ссылка на группу 20, а не на ссылку на группу 2 за которым следует буквальный символ "0". Обратная ссылка \g < 0 > заменяет всю подстроку, соответствующую RE.
Вы можете передать repl как функцию, которая вычисляет значения для замены обратно в исходную строку.
Ответ 4
Если gedit не является надмножеством Python, он не позволит выполнять операции внутри замены-regex, как вы пытаетесь сделать с (\3-2)
. В любом случае, \3
является строкой, и вам нужно сначала преобразовать с помощью int().
Поэтому вам придется разбить его на отдельный re.search(...), вычислить вставленное pageno, а затем вставить.
Вторая проблема заключается в том, что вы не соответствовали длине страницы '2', вы жестко закодировали ее в = - вы хотите, чтобы ваше регулярное выражение соответствовало ей с начала второй строки?
(Также в любом случае ваше многострочное совпадение будет соответствовать только одной строке после PART, если это то, что вы намеревались.)
Здесь он реализован в обычном регулярном выражении Python:
for (chap,sect,page) in re.finditer(r'^(PART.*)\n(.*\s+(\d+))\n', input, re.M):
print chap, int(page)-2
print sect
(я попытался обернуть это как repl fn paginate_chapter(matchobj)
, не может получить re.sub, чтобы называть это надежно еще...)