Вопрос-Ответ

Escaping regex string

Экранирующая строка регулярного выражения

Я хочу использовать ввод от пользователя в качестве шаблона регулярного выражения для поиска по некоторому тексту. Это работает, но как я могу обрабатывать случаи, когда пользователь вводит символы, имеющие значение в регулярном выражении?

Например, пользователь хочет выполнить поиск по слову(s): движок регулярных выражений примет (s) как группу. Я хочу, чтобы он обрабатывал это как строку "(s)" . Я могу запустить replace пользовательский ввод и заменить ( на \( и ) на \), но проблема в том, что мне нужно будет выполнять замену для каждого возможного символа регулярного выражения.

Вы знаете какой-нибудь способ получше?

Переведено автоматически
Ответ 1

Используйте для этого функцию re.escape():

4.2.3 re Содержимое модуля


escape(строка)


Возвращает строку со всеми не алфавитно-цифровыми символами с обратной косой чертой; это полезно, если вы хотите сопоставить произвольную строку-литерал, в которой могут содержаться метасимволы регулярных выражений.


Упрощенный пример: найдите любое вхождение предоставленной строки, за которым необязательно следует 's', и верните объект соответствия.

def simplistic_plural(word, text):
word_or_plural = re.escape(word) + 's?'
return re.match(word_or_plural, text)
Ответ 2

Вы можете использовать re.escape():


re.escape(string) Возвращает строку со всеми не алфавитно-цифровыми символами с обратной косой чертой; это полезно, если вы хотите сопоставить произвольную строку-литерал, в которой могут содержаться метасимволы регулярных выражений.


>>> import re
>>> re.escape('^a.*$')
'\\^a\\.\\*\\$'

Если вы используете версию Python < 3.7, это приведет к экранированию не алфавитно-цифровых символов, которые также не являются частью синтаксиса регулярных выражений.

Если вы используете версию Python < 3.7, но >= 3.3, это приведет к экранированию не алфавитно-цифровых символов, которые не являются частью синтаксиса регулярных выражений, заисключением_ подчеркивания (,,,).

Ответ 3

К сожалению, re.escape() не подходит для строки замены:

>>> re.sub('a', re.escape('_'), 'aa')
'\\_\\_'

Решение состоит в том, чтобы поместить замену в лямбда-выражение:

>>> re.sub('a', lambda _: '_', 'aa')
'__'

потому что возвращаемое значение лямбда-выражения обрабатывается re.sub() как литеральная строка.

Ответ 4

Обычно экранирование строки, которую вы вводите в регулярное выражение, таково, что регулярное выражение рассматривает эти символы буквально. Помните, что обычно вы вводите строки в свой компьютер, и компьютер вставляет определенные символы. Когда вы видите в своем редакторе \n это на самом деле не новая строка, пока анализатор не решит, что это так. Это два символа. Как только вы передадите ее через python print, она отобразится и, таким образом, будет проанализирована как новая строка a, но в тексте, который вы видите в редакторе, это, скорее всего, просто символ обратной косой черты, за которым следует n . Если вы это сделаете \r"\n", то python всегда будет интерпретировать это как необработанную вещь, которую вы ввели (насколько я понимаю). Чтобы еще больше усложнить ситуацию, с регулярными выражениями используется другой синтаксис / грамматика. Анализатор регулярных выражений интерпретирует полученные строки иначе, чем print в python. Я полагаю, именно поэтому нам рекомендуется передавать необработанные строки типа r"(\n+) -- чтобы регулярное выражение получало то, что вы на самом деле ввели. Однако регулярное выражение получит круглую скобку и не будет соответствовать ей как буквальной круглой скобке, если вы не укажете это явно, используя собственные правила синтаксиса регулярного выражения. Для этого вам нужно r"(\fun \( x : nat \) :)" здесь первые скобки не будут сопоставлены, поскольку это группа захвата из-за отсутствия обратной косой черты, но вторая будет сопоставлена как буквенные скобки.

Таким образом мы обычно делаем re.escape(regex), чтобы экранировать то, что мы хотим интерпретировать буквально, т. Е. То, что обычно игнорировалось бы параметром регулярного выражения, например, скобки, пробелы и т.д. будет экранирован. например, код, который у меня есть в моем приложении:

    # escapes non-alphanumeric to help match arbitrary literal string, I think the reason this is here is to help differentiate the things escaped from the regex we are inserting in the next line and the literal things we wanted escaped.
__ppt = re.escape(_ppt) # used for e.g. parenthesis ( are not interpreted as was to group this but literally

например, смотрите эти строки:

_ppt
Out[4]: '(let H : forall x : bool, negb (negb x) = x := fun x : bool =>HEREinHERE)'
__ppt
Out[5]: '\\(let\\ H\\ :\\ forall\\ x\\ :\\ bool,\\negb\\ \\(negb\\ x\\)\\ =\\ x\\ :=\\ fun\\ x\\ :\\ bool\\ =>HEREinHERE\\)'
print(rf'{_ppt=}')
_ppt='(let H : forall x : bool, negb (negb x) = x := fun x : bool =>HEREinHERE)'
print(rf'{__ppt=}')
__ppt='\\(let\\ H\\ :\\ forall\\ x\\ :\\ bool,\\negb\\ \\(negb\\ x\\)\\ =\\ x\\ :=\\ fun\\ x\\ :\\ bool\\ =>HEREinHERE\\)'

двойные обратные косые черты, я полагаю, существуют для того, чтобы регулярное выражение получало буквальную обратную косую черту.


кстати, я удивлен, что он напечатал двойную обратную косую черту вместо одинарной. Если кто-нибудь может прокомментировать это, я был бы признателен. Мне также любопытно, как теперь сопоставить буквенные обратные косые черты в регулярном выражении. Я предполагаю, что это 4 обратных косых черты, но, честно говоря, я ожидал, что потребуется только 2 из-за конструкции raw string r.

2023-09-08 06:30 python regex