Как я могу передать обратный вызов re.sub, но все еще вставляя захваты совпадений? - PullRequest
7 голосов
/ 16 марта 2020

Рассмотрим:

text = "abcdef"
pattern = "(b|e)cd(b|e)"

repl = [r"\1bla\2", r"\1blabla\2"]
text = re.sub(pattern, lambda m: random.choice(repl), text)

Я хочу заменить совпадения случайным образом на записи списка repl. Но при использовании lambda m: random.choice(repl) в качестве обратного вызова он не заменяет \1, \2 et c. с его перехватами, возвращая "\1bla\2" в виде простого текста.

Я попытался найти re.py о том, как они это делают внутри, так что я мог бы вызвать та же самая внутренняя функция, но она не кажется тривиальной.

Приведенный выше пример возвращает a\1bla\2f или a\1blabla\2f, тогда как abblaef или abblablaef являются допустимыми параметрами в моем случае.

Обратите внимание, что я использую функцию, потому что в случае нескольких совпадений, таких как text = "abcdef abcdef", она должна случайным образом выбирать замену из repl для каждого совпадения - вместо использования одной и той же замены для всех совпадений.

Ответы [ 3 ]

8 голосов
/ 16 марта 2020

Если вы передадите функцию, вы потеряете автоматический c выход из обратных ссылок. Вы просто получаете объект соответствия и должны выполнять работу. Таким образом, вы могли бы:

Выбрать строку в регулярном выражении, а не передавать функцию:

text = "abcdef"
pattern = "(b|e)cd(b|e)"

repl = [r"\1bla\2", r"\1blabla\2"]
re.sub(pattern, random.choice(repl), text)
# 'abblaef' or 'abblablaef'

Или написать функцию, которая обрабатывает объект сопоставления и допускает более сложную обработку. Вы можете воспользоваться expand для использования обратных ссылок:

text = "abcdef abcdef"
pattern = "(b|e)cd(b|e)"

def repl(m):
    repl = [r"\1bla\2", r"\1blabla\2"]           
    return m.expand(random.choice(repl))


re.sub(pattern, repl, text)

# 'abblaef abblablaef' and variations

Вы можете, или, конечно, поместить эту функцию в лямбду:

repl = [r"\1bla\2", r"\1blabla\2"]
re.sub(pattern, lambda m: m.expand(random.choice(repl)), text)
1 голос
/ 16 марта 2020

Одним из способов сделать это (и обеспечить случайную замену) является вложение вызовов на re.sub:

text = "abcdef abcdef"
pattern = "(b|e)cd(b|e)"

repl = [r"\1bla\2", r"\1blabla\2"]
text = re.sub(pattern, lambda m: re.sub(r'\\(\d+)', lambda m1: m.group(int(m1.group(1))), random.choice(repl)), text)

print(text)

Выходные данные варьируются в пределах

abblaef abblaef
abblaef abblablaef
abblablaef abblaef
abblablaef abblablaef

Оказывается, мой вложенный вызов был в основном эквивалентом m.expand, как описано в Ответ Марка Мейера .

0 голосов
/ 22 марта 2020

В этом примере группы захвата возвращаются туда, где они были без изменений. Поэтому измените шаблон, чтобы использовать прогнозирование, и вместо этого посмотрите за утверждениями:

replacements = ['bla', 'blabla']
re.sub(r"(?<=b|e)cd(?=b|e)", lambda mo:random.choice(replacements), text)

Это соответствует cd, если перед ним стоит b|e, а затем b|e.

В качестве альтернативы Функция замены получает объект соответствия, поэтому он имеет доступ ко всем группам соответствия:

re.sub(pattern, lambda mo:f"{mo[1]}{random.choice(replacements)}{mo[2]}", text)

, где mo - объект соответствия, mo[1] - первая группа захвата и mo[2] - вторая. .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...