“原始字符串正则表达式”到底是什么?如何使用?


73

regex上的python文档中,关于'\'字符:

解决方案是将Python的原始字符串表示法用于正则表达式模式。反斜杠不会以任何特殊方式处理以开头的字符串文字'r'。所以,r"\n"是包含两个字符的字符串'\''n',虽然"\n"是包含一个换行符一个一个字符的字符串。通常,模式将使用此原始字符串表示法在Python代码中表示。

这个原始的字符串表示法是什么?如果您使用原始字符串格式,这意味着"*"将其视为原义字符而不是零个或多个指示符吗?这显然是不对的,否则正则表达式将完全失去其功能。但是,如果它是原始字符串,那么如果"\n"实际上是反斜杠和,它将如何识别换行符"n"

我不懂

编辑赏金:

我试图了解原始字符串正则表达式如何与换行符,制表符和字符集匹配,例如,\w对于单词或\d数字或其他所有字符,如果原始字符串模式不能识别反斜杠是普通字符以外的其他东西。我真的可以使用一些很好的例子。


11
原始字符串与Python处理字符串的方式有关。它与正则表达式无关。由于它们的特性,将它们用于正则表达式很方便。
Felix Kling 2012年

Answers:


92

Zarkonnen的回答确实回答了您的问题,但没有直接回答。让我尝试更直接一些,看看我是否可以从扎尔肯恩那里获得赏金。

如果您停止使用术语“原始字符串正则表达式”和“原始字符串模式”,则可能会更容易理解。这些术语融合了两个独立的概念:Python源代码中特定字符串的表示形式,以及该字符串表示的正则表达式。

实际上,将它们视为两种不同的编程语言(每种都有自己的语法)会很有帮助。Python语言具有源代码,该源代码除其他外可以构建具有某些内容的字符串,并调用正则表达式系统。正则表达式系统具有驻留在字符串对象中并与字符串匹配的源代码。两种语言都使用反斜杠作为转义字符。

首先,了解字符串是一个字符序列(即字节或Unicode代码点;这里的区别并不重要)。有多种方法可以在Python源代码中表示字符串。一个原始字符串仅仅是这些表象之一。如果两个表示产生相同的字符序列,则它们会产生等效的行为。

想象一个2字符的字符串,由反斜杠字符和n字符组成。如果您知道反斜杠的字符值为92,n的字符值为110,则此表达式将生成我们的字符串:

s = chr(92)+chr(110)
print len(s), s

2 \n

常规的Python字符串表示法"\n"不会生成此字符串。而是生成带有换行符的单字符字符串。在Python文档2.4.1。字符串文字说:“反斜杠(\)字符用于转义具有特殊含义的字符,例如换行符,反斜杠本身或引号字符。”

s = "\n"
print len(s), s

1 
 

(请注意,在此示例中看不到换行符,但是如果仔细看,您会在“ 1”之后看到空白行。)

要获得两个字符的字符串,我们必须使用另一个反斜杠字符来转义原始反斜杠字符的特殊含义:

s = "\\n"
print len(s), s

2 \n

如果要表示其中包含许多反斜杠字符的字符串怎么办?Python文档2.4.1。字符串文字继续,“字符串文字可以选择以字母'r'或'R'开头;此类字符串称为原始字符串,并使用不同的规则来解释反斜杠转义序列。” 这是我们的两个字符的字符串,使用原始字符串表示形式:

s = r"\n"
print len(s), s

2 \n

因此,我们有三种不同的字符串表示形式,它们都给出相同的字符串或字符序列:

print chr(92)+chr(110) == "\\n" == r"\n"
True

现在,让我们来看一下正则表达式。在Python文档,7.2。re正则表达式操作说:“正则表达式使用反斜杠字符('\')表示特殊形式或允许使用特殊字符而无需调用特殊含义。这与Python在同一目的中出于相同目的使用同一字符相冲突。字符串文字...”

如果要与换行符匹配的Python正则表达式对象,则需要2个字符的字符串,该字符串由反斜杠字符和n字符组成。以下代码行均将prog设置为可识别换行符的正则表达式对象:

prog = re.compile(chr(92)+chr(110))
prog = re.compile("\\n")
prog = re.compile(r"\n")

因此,为什么“通常使用这种原始字符串表示法在Python代码中表示模式”。?因为正则表达式通常是静态字符串,所以方便地表示为字符串文字。从正则表达式包含反斜杠字符时,原始字符串是一种方便的选择,从可用的不同字符串文字符号中选择。

问题

:那表情re.compile(r"\s\tWord")呢?:通过将字符串与正则表达式编译分开,并分别理解它们,会更容易理解。

s = r"\s\tWord"
prog = re.compile(s)

该字符串s包含八个字符:反斜杠s反斜杠t和四个字符Word

:制表符和空格字符会怎样?:在Python语言级别,字符串s没有制表符空格字符。它从四个大字:反斜杠小号反斜杠牛逼。同时,正则表达式系统将该字符串视为正则表达式语言中的源代码,其含义是“匹配由空格字符,制表符和四个字符组成的字符串Word

:如果将其当作反斜杠和反斜杠t对待,您如何匹配它们?:如果将“ you”和“ that”这两个词更具体地表达,问题可能会更清楚:正则表达式系统如何匹配表达式backlash-s和backslash-t?作为“任何空白字符”和“制表符”。

:或者如果您有3个字符的字符串反斜杠-n-换行符,该怎么办?:在Python语言中,可以将3个字符的字符串反斜杠-n-换行符表示为常规字符串"\\n\n",或原始加常规字符串r"\n" "\n"或其他方式。当找到任何两个连续的换行符时,正则表达式系统将匹配3个字符的字符串反斜杠-n-换行符

注意:所有示例和文档参考均针对Python 2.7。

更新:合并了@Vladislav Zorov和@ m.buettner的答案以及@Aerovistae的后续问题的说明。


那么re.compile(r“ \ s \ tWord”)呢?制表符和空格字符会怎样?如果将其视为反斜杠-s和反斜杠-t,您如何匹配它们?或者,如果您有3个字符的字符串反斜杠-n-换行符,该怎么办?然后怎样呢?
临时用户名

1
@Aerovistae在编译字符串时将其视为反斜杠s,反斜杠t。这四个字符被交给正则表达式引擎,该引擎解析字符串并知道它必须匹配空格和制表符。如果您使用了普通(非原始)字符串,\ s可能会s以字符串中的结尾,\t并成为一个制表符。现在只有两个字符传递给正则表达式引擎。尽管引擎可能仍然可以匹配制表符,但现在它将尝试匹配其s前面的。
Martin Ender 2012年

2
ord(92)只会引发一个TypeError,因为92它不是字符串。您可能是说chr(92)(或也许unichr(92))?
abarnert

谢谢,@ abarnert!我测试了代码,发现我键入了ord()而不是chr()。我想我未能将更正移回答案。更正了我的答案。
Jim DeLaHunt 2012年

2
嘿@JimDeLaHunt我想说的是,一年后我回来阅读了这篇文章,终于在不同的背景下理解了原始字符串,现在我可以看到您的解释很清楚。我认为当时我对此有某种巨大的思维障碍……现在我在上面教课!再次感谢。
临时用户

16

这些问题中的大多数都有很多单词,也许很难找到特定问题的答案。

如果您使用常规字符串,并向RegEx解析器传递类似“ \ t”的模式,Python会将其转换为带有制表符字节(0x09)的缓冲区。

如果您使用原始字符串,并且将诸如r“ \ t”之类的模式传递给RegEx解析器,则Python不会做任何解释,它将创建一个包含两个字节的缓冲区:'\'和't'。(0x5c,0x74)。

RegEx解析器知道如何处理序列'\ t'-它与制表符匹配。它还知道如何处理0x09字符-该字符也与制表符匹配。在大多数情况下,结果将无法区分。

因此,了解正在发生的事情的关键是认识到这里使用了两个解析器。第一个是Python解析器,它将字符串文字(或原始字符串文字)转换为字节序列。第二个是Python的正则表达式解析器,它将字节序列转换为已编译的正则表达式。


5

使用普通字符串编写包含a的\正则表达式的问题是,您最终不得不\\为每一个编写\。因此,字符串文字"stuff\\things"r"stuff\things"产生相同的字符串。如果您想编写一个与反斜杠匹配的正则表达式,这将特别有用。

使用普通字符串,与该字符串匹配的正则表达式\"\\\\"

为什么?因为我们必须转义\两次:一次用于正则表达式语法,一次用于字符串语法。

您可以使用三引号将换行符包括在内,如下所示:

r'''stuff\
things'''

请注意,通常,python会将\-newline视为行继续,但原始字符串中并非如此。另请注意,反斜杠仍然在原始字符串中转义引号,但保留在其自身中。因此,原始字符串文字r"\""会生成字符串\"。这意味着您不能以反斜杠结束原始字符串文字。

有关更多信息,请参见Python文档的词法分析部分


1
不是每个\都真正\\。'\ d'被解释为反斜杠(?),后跟d。
nhahtdh 2012年

2
@Aerovistae:使用r'''something<enter>onnewline'''<enter>表示按Enter键。不太漂亮,因此您可以在此处使用字符串连接吗?
nhahtdh 2012年

2
实际上,由于如何处理原始字符串,r"stuff\"是一个错误。
伊格纳西奥·巴斯克斯

@ IgnacioVazquez-Abrams&nhahtdh解决了这个问题!
Zarkonnen 2012年

@Aerovistae的re.match(r'1\n2', string)意思是re.match('1\\n2', string),所以\n不是由python解释,而是由regex解析器解释-实际上,它给出的结果与简单的结果相同,re.match('1\n2', string)因为regex解析器可以很好地处理python给定的未转义的换行符(至少在我的Python 3测试中)
Aprillion

4

您似乎在为RegEx不是Python的一部分,而是拥有自己的解析器和编译器的另一种编程语言而烦恼。原始字符串帮助你得到一个正则表达式的“源代码”安全的正则表达式解析器,它将然后分配含义的字符序列一样\d\w\n,等...

存在问题是因为Python和RegExps\用作转义字符,这是一个巧合-某些语言具有其他转义字符(例如换行符为“`n”,但即使在那儿也必须使用“ \ n”在RegExps中)。优点是您不需要区分这些语言中的原始字符串和非原始字符串,它们不会尝试转换文本并对其进行分割,因为它们会对不同的转义序列做出反应。


1

相关的Python手册部分(“字符串和字节文字”)对原始字符串文字有清晰的解释:

字符串和字节文字都可以选择以字母“ r”或“ R”为前缀;这样的字符串称为原始字符串,并将反斜杠视为原义字符。结果,在字符串文字中,原始字符串中的'\ U'和'\ u'转义符未得到特殊处理。鉴于Python 2.x的原始unicode文字的行为与Python 3.x的不同,因此不支持'ur'语法。

3.3版中的新功能:原始字节文字的'rb'前缀已添加为'br'的同义词。

版本3.3中的新增功能:重新引入了对Unicode旧式文字(u'value')的支持,以简化对双重Python 2.x和3.x代码库的维护。有关更多信息,请参见PEP 414。

在三重引用的字符串中,允许(并保留)未转义的换行符和引号,但一行中的三个未转义的引号会终止该字符串。(“引号”是用于打开字符串的字符,即“或”。)

除非存在'r'或'R'前缀,否则将根据类似于标准C使用的规则来解释字符串中的转义序列。可识别的转义序列为:

转义序列含义注释

\ newline反斜杠和换行符被忽略
\反斜杠()
\'单引号(')
\“双引号(”)
\ a ASCII Bell(BEL)
\ b ASCII Backspace(BS)
\ f ASCII Formfeed(FF)
\ n ASCII Linefeed( LF)
\ r ASCII回车
符(CR)\ t ASCII水平制表符(TAB)\ v ASCII垂直制表符(VT)
\ ooo八进制值ooo(1,3)
\ xhh十六进制值hh(2,3)的字符

仅在字符串文字中识别的转义序列为:

转义序列含义注释\ N {name} Unicode数据库中名为name的字符(4)\ uxxxx具有16位十六进制值xxxx的字符(5)\ Uxxxxxxxx具有32位十六进制值xxxxxxxx的字符(6)

笔记:

  1. 与标准C一样,最多可以接受三个八进制数字。

  2. 与标准C中不同,恰好需要两个十六进制数字。

  3. 在字节文字中,十六进制和八进制转义表示具有给定值的字节。在字符串文字中,这些转义符表示具有给定值的Unicode字符。

  4. 在版本3.3中更改:添加了对名称别名[1]的支持。

  5. 可以使用此转义序列对形成代理对的一部分的各个代码单元进行编码。确实需要四个十六进制数字。

  6. 任何Unicode字符都可以用这种方式编码,但是如果Python被编译为使用16位代码单元(默认值),则基本多语言平面(BMP)之外的字符将使用代理对进行编码。恰好需要八个十六进制数字。

与标准C不同,所有无法识别的转义序列都保留在字符串中不变,即,反斜杠保留在字符串中。(此行为在调试时非常有用:如果转义序列输入错误,则更容易将输出识别为中断。)还要注意的是,仅在字符串文字中识别的转义序列属于无法识别的字节转义类别。文字。

即使在原始字符串中,字符串引号也可以使用反斜杠进行转义,但是反斜杠仍保留在字符串中;例如,r“ \”“是由两个字符组成的有效字符串文字:反斜杠和双引号; r” \“不是有效的字符串文字(即使原始字符串也不能以奇数个反斜杠结尾)。具体来说,原始字符串不能以单个反斜杠结尾(因为反斜杠会转义以下引号字符)。请注意,单个反斜杠后跟换行符会被解释为这两个字符是字符串的一部分,而不是换行符。



0

原始字符串不会影响python正则表达式中的特殊序列,例如\ w,\ d。它仅影响转义序列,例如\ n。因此,大多数时候我们是否在前面写r都没关系。

我认为这是大多数初学者正在寻找的答案。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.