Необработанная строка и регулярное выражение в Python

Меня смущает необработанная строка в следующем коде:

import re

text2 = 'Today is 11/27/2012. PyCon starts 3/13/2013.'
text2_re = re.sub(r'(\d+)/(\d+)/(\d+)', r'\3-\1-\2', text2)
print (text2_re) #output: Today is 2012-11-27. PyCon starts 2013-3-13.

print (r'(\d+)/(\d+)/(\d+)') #output: (\d+)/(\d+)/(\d+)

Насколько я понимаю, необработанная строка без r, \ рассматривается как escape-символ; с r обратная косая черта \ трактуется буквально сама по себе (обратная косая черта).

Однако то, что я не могу понять в приведенном выше коде, так это то, что:

  • В строке 5 регулярного выражения, даже если есть r, \ d внутри рассматривается как одно число [0-9] вместо этого. из одной обратной косой черты \ плюс одна буква d.
  • Во второй строке печати 8 все символы рассматриваются как необработанные строки.

В чем разница?

Дополнительная версия:

Я сделал следующие четыре варианта, с r или без него:

import re

text2 = 'Today is 11/27/2012. PyCon starts 3/13/2013.'
text2_re = re.sub(r'(\d+)/(\d+)/(\d+)', r'\3-\1-\2', text2)
text2_re1 = re.sub('(\d+)/(\d+)/(\d+)', r'\3-\1-\2', text2)
text2_re2 = re.sub(r'(\d+)/(\d+)/(\d+)', '\3-\1-\2', text2)
text2_re3 = re.sub('(\d+)/(\d+)/(\d+)', '\3-\1-\2', text2)

print (text2_re)
print (text2_re1)
print (text2_re2)
print (text2_re3)

И получите следующий результат:

Не могли бы вы конкретно объяснить эти четыре ситуации?


person fluency03    schedule 11.05.2015    source источник


Ответы (4)


Вас смущает разница между строкой и строковым литералом.

Строковый литерал - это то, что вы помещаете между " или ', и интерпретатор python анализирует эту строку и помещает ее в память. Если вы пометите свой строковый литерал как необработанный строковый литерал (используя r'), тогда интерпретатор python не изменит представление этой строки перед тем, как поместить ее в память, но после того, как они были проанализированы, они сохраняются точно так же.

Это означает, что в памяти нет такой вещи, как необработанная строка. Обе следующие строки одинаково хранятся в памяти без понятия, были ли они необработанными или нет.

r'a regex digit: \d'  # a regex digit: \d
'a regex digit: \\d'  # a regex digit: \d

Обе эти строки содержат \d, и нельзя сказать, что это исходная строка. Поэтому, когда вы передаете эту строку модулю re, он видит, что есть \d, и видит в нем цифру, потому что модуль re не знает, что строка пришла из необработанного строкового литерала.

В вашем конкретном примере, чтобы получить буквальную обратную косую черту, за которой следует литерал d, вы должны использовать \\d следующим образом:

import re

text2 = 'Today is 11/27/2012. PyCon starts 3/13/2013.'
text2_re = re.sub(r'(\\d+)/(\\d+)/(\\d+)', r'\3-\1-\2', text2)
print (text2_re) #output: Today is 11/27/2012. PyCon starts 3/13/2013.

В качестве альтернативы, без использования необработанных строк:

import re

text = 'Today is 11/27/2012. PyCon starts 3/13/2013.'
text_re = re.sub('(\\d+)/(\\d+)/(\\d+)', '\\3-\\1-\\2', text2)
print (text_re) #output: Today is 2012-11-27. PyCon starts 2013-3-13.

text2 = 'Today is 11/27/2012. PyCon starts 3/13/2013.'
text2_re = re.sub('(\\\\d+)/(\\\\d+)/(\\\\d+)', '\\3-\\1-\\2', text2)
print (text2_re) #output: Today is 11/27/2012. PyCon starts 3/13/2013.

Я надеюсь, что это немного поможет.

Изменить: я не хотел усложнять ситуацию, но поскольку \d не является допустимой escape-последовательностью, python не меняет ее, поэтому '\d' == r'\d' верно. Поскольку \\ является допустимой escape-последовательностью, она изменяется на \, поэтому вы получаете поведение '\d' == '\\d' == r'\d'. Иногда строки сбивают с толку.

Edit2: Чтобы ответить на ваше изменение, давайте рассмотрим каждую строку отдельно:

text2_re = re.sub(r'(\d+)/(\d+)/(\d+)', r'\3-\1-\2', text2)

re.sub получает две строки (\d+)/(\d+)/(\d+) и \3-\1-\2. Надеюсь, теперь все пойдет так, как вы ожидаете.

text2_re1 = re.sub('(\d+)/(\d+)/(\d+)', r'\3-\1-\2', text2)

Опять же (поскольку \d не является допустимым побегом строки, он не изменяется, см. Мое первое изменение) re.sub получает две строки (\d+)/(\d+)/(\d+) и \3-\1-\2. Поскольку \d не изменяется интерпретатором python r'(\d+)/(\d+)/(\d+)' == '(\d+)/(\d+)/(\d+)'. Если вы понимаете мою первую правку, то, надеюсь, вы поймете, почему эти два случая ведут себя одинаково.

text2_re2 = re.sub(r'(\d+)/(\d+)/(\d+)', '\3-\1-\2', text2)

Этот случай немного отличается, потому что \1, \2 и \3 - все допустимые escape-последовательности, они заменены на символ Юникода, десятичное представление которого задается числом. Это довольно сложно, но в основном сводится к следующему:

\1  # stands for the ascii start-of-heading character
\2  # stands for the ascii start-of-text character
\3  # stands for the ascii end-of-text character

Это означает, что re.sub получает первую строку, как это было в первых двух примерах ((\d+)/(\d+)/(\d+)), но на самом деле вторая строка равна <start-of-heading>/<start-of-text>/<end-of-text>. Таким образом, re.sub заменяет совпадение этой второй строкой точно, но поскольку ни один из трех (\1, \2 или \3) не является печатаемым символом, python вместо этого просто печатает символ-заполнитель запаса.

text2_re3 = re.sub('(\d+)/(\d+)/(\d+)', '\3-\1-\2', text2)

Это ведет себя как третий пример, потому что r'(\d+)/(\d+)/(\d+)' == '(\d+)/(\d+)/(\d+)', как объяснено во втором примере.

person Sean1708    schedule 11.05.2015
comment
Не могли бы вы также пояснить дополнительную часть вопроса? - person fluency03; 11.05.2015
comment
Я попытался их объяснить. Это довольно сложное поведение, поэтому, надеюсь, я вас не запутал еще больше. - person Sean1708; 11.05.2015
comment
REP может быть строкой или функцией; если это строка, то обрабатываются любые escape-символы в ней. То есть \ n преобразуется в один символ новой строки, \ r преобразуется в возврат каретки и т. Д. docs.python.org/3/library/re.html#re. sub - person JinSnow; 09.02.2017
comment
вы можете проверить свое регулярное выражение здесь (в python и других) regex101.com (проверьте вкус Python) - person JinSnow; 09.02.2017

Вы должны делать различие между интерпретатором python и модулем re.

В python обратная косая черта, за которой следует символ, может означать специальный символ, если строка не является исходной. Например, \n будет означать символ новой строки, \r будет означать возврат каретки, \t будет означать символ табуляции, \b представляет неразрушающий возврат. Само по себе \d в строке Python ничего особенного не означает.

Однако в регулярном выражении есть набор символов, которые в противном случае не всегда что-либо значили бы в Python. Но в этом загвоздка, «не всегда». Одна из вещей, которую можно неправильно истолковать, - это \b, который в python является обратным пространством, а в регулярном выражении означает границу слова. Это означает, что если вы передадите неотрисованный \b части регулярного выражения регулярного выражения, это \b будет заменено обратным пространством перед, которое будет передано функции регулярного выражения, и это не будет означать, что вещь там. Таким образом, вы должны абсолютно передать b с его обратной косой чертой, и для этого вы либо избегаете обратной косой черты, либо обрабатываете строку.

Возвращаясь к вашему вопросу о \d, \d не имеет никакого особого значения в python, поэтому он остается нетронутым. То же \d, переданное как регулярное выражение, преобразуется механизмом регулярных выражений, который является отдельной сущностью для интерпретатора Python.


За редактирование вопроса:

import re

text2 = 'Today is 11/27/2012. PyCon starts 3/13/2013.'
text2_re = re.sub(r'(\d+)/(\d+)/(\d+)', r'\3-\1-\2', text2)
text2_re1 = re.sub('(\d+)/(\d+)/(\d+)', r'\3-\1-\2', text2)
text2_re2 = re.sub(r'(\d+)/(\d+)/(\d+)', '\3-\1-\2', text2)
text2_re3 = re.sub('(\d+)/(\d+)/(\d+)', '\3-\1-\2', text2)

print(text2_re)
print(text2_re1)
print(text2_re2)
print(text2_re3)

Первые два должны быть простыми. re.sub делает свое дело, сопоставляя числа и косую черту и заменяя их дефисами в другом порядке. Поскольку \d не имеет особого значения в python, \d передается в re.sub независимо от того, является ли выражение необработанным или нет.

Третье и четвертое случаются из-за того, что вы не обработали строки для выражения замены. \1, \2 и \3 имеют особое значение в python, представляя белый (или незаполненный) смайлик, черный (заполненный) смайлик и сердце соответственно (если символы не могут быть отображены, вы получаете эти «блоки символов»). Таким образом, вместо замены захваченными группами вы заменяете строки определенными символами.

введите описание изображения здесь

person Jerry    schedule 11.05.2015

Я чувствую, что приведенные выше ответы слишком усложняют его. Если вы используете re.search(), отправляемая вами строка анализируется на двух уровнях:

  1. Python интерпретирует \ символы, которые вы вводите через этот фильтр.

  2. Затем регулярное выражение интерпретирует \ символы, которые вы пишете через собственный фильтр .

Они происходят именно в таком порядке.

"Необработанный" строковый синтаксис r"\nlolwtfbbq" предназначен для обхода интерпретатора Python, он не влияет на re:

>>> print "\nlolwtfbbq"

lolwtfbbq
>>> print r"\nlolwtfbbq"
\nlolwtfbbq
>>>

Обратите внимание, что в первом примере печатается новая строка, но фактические символы \ и n печатаются во втором, потому что они необработанные.

Любые строки, которые вы отправляете на re, проходят через интерпретатор регулярных выражений, поэтому, чтобы ответить на ваш конкретный вопрос, \d означает «цифры 0–9» в регулярном выражении.

person Locane    schedule 11.05.2018

Не все \ вызовут проблемы. Интерпретатор Python имеет некоторые встроенные функции, такие как \b и т. Д. Итак, теперь, если r отсутствует, Python будет рассматривать \b как свой собственный литерал, а не word boundary для регулярного выражения. Когда он используется с режимом r (rawstring), тогда \b остается как есть. Это на языке непрофессионала. Не особо разбираюсь в технике. \d не является специальной встроенной функцией в Python, так что это будет безопасно даже без режима r.

Здесь вы можете просмотреть список. Это список, который Python понимает и интерпретирует, например \b, \n, а не \d.

В первом print интерпретация \d выполняется модулем регулярных выражений, а не Python. Во втором print это делает Python. Поскольку он находится в режиме r, он будет сохранен как есть.

person vks    schedule 11.05.2015
comment
Что вы имеете в виду, интерпретация выполняется с помощью регулярного выражения или с помощью python? В чем разница? - person fluency03; 11.05.2015
comment
@ fluency_03 \d ничего не значит для python. Это модуль regex, который знает, что \d есть [0-9]. В тех же строках python знает _6 _, _ 7_, поэтому, когда он находит их, он их интерпретирует. Итак, если вы хотите, чтобы Python не интерпретировал их, вы вставляете все в r режим. - person vks; 11.05.2015