Экранирующая строка регулярного выражения
Я хочу использовать ввод от пользователя в качестве шаблона регулярного выражения для поиска по некоторому тексту. Это работает, но как я могу обрабатывать случаи, когда пользователь вводит символы, имеющие значение в регулярном выражении?
Например, пользователь хочет выполнить поиск по слову(s)
: движок регулярных выражений примет (s)
как группу. Я хочу, чтобы он обрабатывал это как строку "(s)"
. Я могу запустить replace
пользовательский ввод и заменить (
на \(
и )
на \)
, но проблема в том, что мне нужно будет выполнять замену для каждого возможного символа регулярного выражения.
Вы знаете какой-нибудь способ получше?
Переведено автоматически
Ответ 1
Используйте для этого функцию re.escape()
:
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.