Python "re"包,"raw"字符串的奇怪现象
我遇到了一个现象,搞不清楚怎么回事,也没在网上找到相关的信息:
如果我输入:
>>> if re.search(r'\n',r'this\nis\nit'):<br>
... print 'found it!'<br>
... else:<br>
... print "didn't find it"<br>
...
我会得到:
didn't find it!
但是,如果我输入:
>>> if re.search(r'\\n',r'this\nis\nit'):<br>
... print 'found it!'<br>
... else:<br>
... print "didn't find it"<br>
...
那么我会得到:
found it!
(第一个例子中的 r'\n' 只有一个反斜杠,而第二个例子中的 r'\\n' 有两个反斜杠……即使在这个解释器中,它也会去掉一个。)
我可以猜测发生了什么,但我不太明白为什么会这样:在第一个例子中,我需要转义两个东西:正则表达式和特殊字符串。“原始”字符串让我可以转义特殊字符串,但不能转义正则表达式。
而在第二个字符串中,永远不会有正则表达式,因为它是要被匹配的字符串。所以只需要转义一次。
不过,有些地方我觉得不太一致:在第一个例子中,我怎么才能确保字符真的被当作字面意思来处理呢?我可以输入 rr'' 吗?还是说我必须确保转义两次?
类似地,我怎么才能确保一个变量被当作字面意思(或者说它不是被当作字面意思)?比如,如果我有一个变量 tmp = 'this\nis\nmy\nhome',我真的想找到一个斜杠和一个 'n' 的字面组合,而不是换行符,该怎么做?
谢谢!
Mike
3 个回答
正则表达式对反斜杠有自己特别的理解,比如像 \d
这样的字符类。如果你想要一个真正的反斜杠字符,你实际上需要输入两个反斜杠。因为你是在把正则表达式和字符串进行比较,所以这两者并不完全相同。
原始字符串只是为了方便使用,如果再搞个双原始字符串,那就太复杂了。
在字符串中处理特殊字符和在正则表达式中处理特殊字符是两回事。行字符串修饰符只影响前者。
从技术上讲,re.search
接受两个字符串,第一个字符串会通过 re.compile
传给正则表达式构建器。编译后的正则表达式对象用于在简单字符串中搜索模式。而第二个字符串不会被编译,因此它不受正则表达式特殊字符规则的影响。
如果正则表达式构建器在处理字符串字面量后接收到一个 \n
,它会把这个序列转换成换行符。如果你想匹配这个序列,就必须对它进行转义。
这样做的原因是,正则表达式并不是语言语法的一部分。它们实际上是在标准库中的 re
模块里处理的,使用的是语言的常见构建块。
re.compile
函数使用的特殊字符和转义规则与大多数常用的正则表达式实现是兼容的。然而,Python 解释器并不知道整个正则表达式的概念,也不知道一个字符串字面量是否会被编译成正则表达式对象。因此,Python 无法提供你所建议的那种语法简化。
re.search(r'\n', r'this\nis\nit')
正如你所说,“第二个字符串里永远不会有正则表达式。”所以我们需要换个角度来看这两个字符串:第一个字符串是一个正则表达式,第二个只是一个普通字符串。通常情况下,你的第二个字符串不会是原始的,所以里面的反斜杠是Python的转义符,而不是正则表达式的转义符。
所以,第一个字符串包含一个字面上的“\”和一个“n”。正则表达式解析器会把它理解为换行符(文档: “Python字符串字面量支持的大多数标准转义符也被正则表达式解析器接受”)。因此,你的正则表达式会去寻找一个换行符。
而你的第二个字符串是“this”后面跟着一个字面上的“\”和一个“n”。所以这个字符串里并没有实际的换行符。你的正则表达式不会匹配。
至于你的第二个正则表达式:
re.search(r'\\n', r'this\nis\nit')
这个版本可以匹配,因为你的正则表达式包含三个字符:一个字面上的“\”,另一个字面上的“\”和一个“n”。正则表达式解析器会把两个反斜杠理解为一个“\”字符,后面跟着一个“n”。所以你的正则表达式会去寻找一个“\”后面跟着一个“n”,这个在字符串中是存在的。但这并没有什么用,因为它和换行符没有关系。
你最有可能想要的是去掉第二个字符串中的r
,这样它就会被当作一个普通的Python字符串来处理。
re.search(r'\n', 'this\nis\nit')
在这种情况下,你的正则表达式(和之前一样)是在寻找一个换行符。而且,它找到了,因为第二个字符串包含了“this”后面跟着一个换行符。