Смущен обратной косой чертой в регулярных выражениях

Я путаюсь с обратной косой чертой в регулярных выражениях. В пределах регулярного выражения a \ имеет особое значение, например. \d означает десятичную цифру. Если вы добавили обратную косую черту перед обратным слэшем, это особое значение теряется. В regex-howto можно прочитать:

Возможно, самым важным метасимволом является обратная косая черта, \. Как и в строковых литералах Python, обратная косая черта может сопровождаться различными символами для сигнализации различных специальных последовательностей. Его также использовали, чтобы избежать всех метасимволов, чтобы вы все еще могли сопоставлять их с шаблонами; например, если вам нужно соответствовать [ или \, вы можете перед ними обратную косую черту, чтобы удалить их особый смысл: \[ или \\.

Итак print(re.search('\d', '\d')) дает None, потому что \d соответствует любой десятичной цифре, но в \d нет ни одного.

Теперь я ожидаю, что print(re.search('\\d', '\d')) будет соответствовать \d, но ответ по-прежнему None.

Только print(re.search('\\\d', '\d')) дает в качестве вывода <_sre.SRE_Match object; span=(0, 2), match='\\d'>.

Есть ли у кого-нибудь объяснения?

Ответ 1

Путаница связана с тем, что символ обратной косой черты \ используется в качестве выхода на двух разных уровнях. Во-первых, сам интерпретатор Python выполняет замены для \ до того, как модуль re увидит вашу строку. Например, \n преобразуется в символ новой строки, \t преобразуется в символ табуляции и т.д. Чтобы получить фактический символ \, вы также можете экранировать его, поэтому \\ дает один \. ] характер. Если символ, следующий за \, не является распознанным escape-символом, тогда \ обрабатывается как любой другой символ и проходит через него, но я не рекомендую зависеть от этого. Вместо этого всегда избегайте своих \ персонажей, удваивая их, то есть \\.

Если вы хотите увидеть, как Python расширяет ваши строки, просто распечатайте строку. Например:

s = 'a\\b\tc'
print(s)

Если s является частью агрегированного типа данных, например, список или кортеж, и если вы напечатаете этот агрегат, Python заключит строку в одинарные кавычки и включит escape-символы \ (в канонической форме), так что будьте внимательны, как печатается ваша строка. Если вы просто напечатаете строку в кавычках в интерпретаторе, она также отобразит ее в кавычках с символами \.

Как только вы узнаете, как кодируется ваша строка, вы можете подумать, что с ней будет делать модуль re. Например, если вы хотите экранировать \ в строке, передаваемой в модуль re, вам нужно будет передать \\ в re, что означает, что вам нужно будет использовать \\\\ в вашем цитируемом Python строка. Строка Python будет заканчиваться на \\, а модуль re будет обрабатывать это как один литеральный символ \.

Альтернативный способ включить символы \ в строки Python - это использовать необработанные строки, например, r'a\b' эквивалентен "a\\b".

Ответ 2

Символ r перед регулярным выражением в вызове search() указывает, что регулярное выражение является необработанной строкой. Это позволяет использовать обратную косую черту в регулярном выражении в качестве обычных символов, а не в escape-последовательности символов. Позвольте мне объяснить...

Прежде чем метод поиска re module обрабатывает переданные ему строки, интерпретатор Python выполняет первоначальный проход по строке. Если в строке присутствуют обратные слэши, интерпретатор Python должен решить, является ли каждый из них частью escape-последовательности Python (например,\n или \t) или нет.

Примечание: на данный момент Python не заботится о том, является ли '\' мета-символом регулярного выражения.

Если за "\" следует распознанный escape-символ Python (t, n и т.д.), То обратная косая черта и escape-символ заменяются фактическим Unicode или 8-битным символом. Например, '\ t' будет заменено символом ASCII для табуляции. В противном случае он передается и интерпретируется как символ '\'.

Подумайте о следующем.

>>> s = '\t'
>>> print ("[" + s  + "]")
>>> [       ]           // an actual tab character after preprocessing

>>> s = '\d'
>>> print ("[" + s  + "]")
>>> [\d]                // '\d' after preprocessing

Иногда мы хотим включить в строку символьную последовательность, которая включает '\', без интерпретации Python как escape-последовательность. Для этого мы избегаем "\" с помощью "\". Теперь, когда Python видит "\", он заменяет две обратные косые черты одним символом "\".

>>> s = '\\t'
>>> print ("[" + s  + "]")
>>> [\t]                // '\t' after preprocessing

После того, как интерпретатор Python передаст обе строки, они будут переданы в метод поиска re module. Метод поиска анализирует строку регулярного выражения, чтобы определить метасимволы регулярного выражения.

Теперь '\' также является специальным метасимволом регулярного выражения и интерпретируется как один, ЕСЛИ он не экранируется во время выполнения метода re search().

Рассмотрим следующий вызов.

>>> match = re.search('a\\t','a\\t')        //Match is None

Здесь совпадений нет. Почему? Давайте посмотрим на строки после того, как интерпретатор Python сделает это.

String 1: 'a\t'
String 2: 'a\t' 

Так почему же матч равен None? Когда search() интерпретирует строку 1, поскольку это регулярное выражение, обратный слеш интерпретируется как метасимвол, а не как обычный символ. Обратная косая черта в строке 2, однако, отсутствует в регулярном выражении и уже была обработана интерпретатором Python, поэтому она интерпретируется как обычный символ.

Поэтому метод search() ищет в строке 'a\t' escape-t, которые не совпадают.

Чтобы исправить это, мы можем сказать, что метод search() не должен интерпретировать '\' как метасимвол. Мы можем сделать это, избежав этого.

Рассмотрим следующий вызов.

>>> match = re.search('a\\\\t','a\\t')          // Match contains 'a\t'

Опять же, давайте посмотрим на строки после того, как интерпретатор Python прошел.

String 1: 'a\\t'
String 2: 'a\t'

Теперь, когда метод search() обрабатывает регулярное выражение, он видит, что второй обратный слеш экранируется первым и не должен рассматриваться как метасимвол. Поэтому он интерпретирует строку как 'a\t', что соответствует строке 2.

Альтернативный способ сделать так, чтобы search() рассматривал символ "\", - поместить r перед регулярным выражением. Это говорит интерпретатору Python НЕ предварительно обрабатывать строку.

Учтите это.

>>> match = re.search(r'a\\t','a\\t')           // match contains 'a\t'

Здесь интерпретатор Python не изменяет первую строку, но обрабатывает вторую строку. Строки, переданные функции search():

String 1: 'a\\t'
String 2: 'a\t'

Как и в предыдущем примере, search интерпретирует '\' как отдельный символ '\', а не как метасимвол, поэтому соответствует строке 2.

Ответ 3

Собственный синтаксический анализ Python (частично) на вашем пути.

Если вы хотите увидеть, что видит re, введите

print '\d'
print '\\d'
print '\\\d'

в командной строке Python. Вы видите, что \d и \\d оба результата приводят к \d, причем последний обрабатывается парсером строк Python.

Если вы хотите избежать каких-либо проблем с ними, используйте необработанные строки, как было предложено re module documentation: r'\\d' приведет к \\d, видимый модулем RE.