Python 中的原始字符串和正则表达式

Raw string and regular expression in Python

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

import re

text2 = 'Today is 11/27/2012. PyCon starts 3/13/2013.'
text2_re = re.sub(r'(\d+)/(\d+)/(\d+)', r'--', 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,反斜杠 \ 被视为本身(反斜杠)。

然而,上面代码中我无法理解的是:

有什么区别?

附加版:

我做了以下四个变化,有或没有r

import re

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

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

并得到以下输出:

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

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

在这里您可以see列表。这是 Python 理解并将解释的列表,例如 \b\n 而不是 \d.

在第一个 print 中,\d 解释是由正则表达式模块而不是 Python 完成的。在第二个 print 中,它由 Python 完成。因为它处于 r 模式,所以它会按原样放置。

python 解释器和 re 模块之间有一个区别。

在 python 中,如果字符串未被原始化,则反斜杠后跟一个字符可能表示特殊字符。例如,\n 表示换行符,\r 表示回车符 return,\t 表示制表符,\b 表示非破坏性退格符。就其本身而言,python 字符串中的 \d 并不意味着任何特殊的东西。

然而,在正则表达式中,有一堆字符在 python 中并不总是意味着什么。但这就是问题所在,'not always'。可能会被误解的一件事是 \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'--', text2)
text2_re1 = re.sub('(\d+)/(\d+)/(\d+)', r'--', text2)
text2_re2 = re.sub(r'(\d+)/(\d+)/(\d+)', '--', text2)
text2_re3 = re.sub('(\d+)/(\d+)/(\d+)', '--', text2)

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

前两个应该很简单。 re.sub 通过匹配数字和正斜杠并以不同的顺序用连字符替换它们来完成它的工作。由于 \d 在 python 中没有任何特殊含义,因此 \d 传递给 re.sub 无论表达式是否原始。

发生第三次和第四次是因为您没有提取替换表达式的字符串。 </code>、<code>在python中有特殊含义,分别代表白色(或未填充)笑脸、黑色(填充)笑脸和心形(如果字符无法显示,你会得到这些 'character boxes')。因此,您不是用捕获的组替换,而是用特定字符替换字符串。

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

字符串字面量是您放在 "' 之间的内容,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'--', 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'--', text2)

re.sub 接收两个字符串 (\d+)/(\d+)/(\d+)--。希望这会像您现在期望的那样运行。

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

再次(因为 \d 不是有效的字符串转义,它不会被更改,请参阅我的第一次编辑) re.sub 接收两个字符串 (\d+)/(\d+)/(\d+)--.因为 \d 不会被 python 解释器 r'(\d+)/(\d+)/(\d+)' == '(\d+)/(\d+)/(\d+)' 改变。如果您理解我的第一次编辑,那么希望您应该理解为什么这两种情况的行为相同。

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

这种情况有点不同,因为</code>、<code></code>都是有效的转义序列,它们被替换为<a href="http://unicode-table.com/en/" rel="noreferrer">unicode character</a>,其十进制表示按数量。这很复杂,但基本上可以归结为:</p> <pre><code> # stands for the ascii start-of-heading character # stands for the ascii start-of-text character # stands for the ascii end-of-text character

这意味着 re.sub 接收第一个字符串,就像它在前两个示例 ((\d+)/(\d+)/(\d+)) 中所做的那样,但第二个字符串实际上是 <start-of-heading>/<start-of-text>/<end-of-text>。因此 re.sub 完全用第二个字符串替换了匹配项,但是因为三个字符串中的 none(</code>、<code></code>)是可打印字符 python 只是打印一个常用的占位符。</p> <pre><code>text2_re3 = re.sub('(\d+)/(\d+)/(\d+)', '--', text2)

这与第三个示例的行为类似,因为 r'(\d+)/(\d+)/(\d+)' == '(\d+)/(\d+)/(\d+)',如第二个示例中所述。

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

  1. Python解释\你写的字符through this filter.

  2. 然后,正则表达式解释\你写的字符through its own filter

它们按此顺序发生。

"raw" 字符串语法 r"\nlolwtfbbq" 用于绕过 Python 解释器,它不会影响 re:

>>> print "\nlolwtfbbq"

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

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

您发送到 re 的任何字符串都会通过正则表达式解释器,因此为了回答您的具体问题,\d 在正则表达式中表示 "digit 0-9"。