7

我对以下代码中的原始字符串感到困惑:

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\被视为转义字符;使用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)

并得到以下输出:

你能具体解释一下这四种情况吗?

4

4 回答 4

14

您对字符串和字符串文字之间的区别感到困惑。

字符串文字是您放在"or之间'的内容,python 解释器解析此字符串并将其放入内存。如果您将字符串文字标记为原始字符串文字(使用r'),则 python 解释器在将其放入内存之前不会更改该字符串的表示,但一旦它们被解析,它们就会以完全相同的方式存储。

这意味着在内存中没有原始字符串之类的东西。以下两个字符串都以相同的方式存储在内存中,不知道它们是否是原始的。

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

这两个字符串都包含\d并且没有什么可以说它来自原始字符串。因此,当您将此字符串传递给re模块时,它会看到有 a\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不是有效的转义序列,python 不会改变它,所以'\d' == r'\d'是真的。因为\\ 它是一个有效的转义序列\,所以你得到了这个行为'\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都是有效的转义序列,它们被替换为unicode 字符,其十进制表示由数字给出。这很复杂,但基本上可以归结为:

\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+)'正如第二个示例中所解释的那样。

于 2015-05-11T10:02:05.940 回答
4

re您必须在 python 解释器和模块之间做出区分。

在 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中有一个特殊的含义,分别代表一个白色(或未填充)的笑脸,一个黑色(填充)的笑脸和一颗心(如果字符不能显示,你会得到这些'字符框')。因此,您不是用捕获的组替换,而是用特定字符替换字符串。

在此处输入图像描述

于 2015-05-11T09:59:09.853 回答
2

我觉得上面的答案过于复杂了。如果您正在运行re.search(),您发送的字符串将通过两层解析:

  1. Python 解释您通过此过滤器编写的 \ 字符。

  2. 然后,正则表达式通过自己的过滤器解释您编写的 \ 字符。

它们按此顺序发生。

“原始”字符串语法r"\nlolwtfbbq"适用于您想要绕过 Python 解释器时,它不会影响re

>>> print "\nlolwtfbbq"

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

请注意,在第一个示例中打印了换行符,但在第二个示例中打印了实际字符\n因为它是原始的。

您发送以re通过正则表达式解释器的任何字符串,因此要回答您的特定问题,\d在正则表达式中表示“数字 0-9”。

于 2018-05-11T17:08:27.280 回答
1

并非所有\都会导致问题。Python 解释器有一些类似的内置函数\b。所以现在如果r不存在,Python 将考虑\b作为它自己的文字而不是word boundary正则表达式。当它与r(rawstring) 模式一起使用时,\b将保持原样。那是通俗的语言。技术含量不多。\d不是python中的特殊内置函数,因此即使没有r模式也是安全的。

在这里你可以看到列表。这是 Python 理解和解释的列表,like\b\nnot \d

首先print\d解释是由正则表达式模块而不是 Python 完成的。第二个print是由 Python 完成的。由于它处于r模式,它将按原样放置。

于 2015-05-11T09:34:53.280 回答