原始字符串、Python和re、普通字符与特殊字符

原始字符串、Python和re、普通字符与特殊字符,python,regex,escaping,backslash,Python,Regex,Escaping,Backslash,关于原始字符串,我遇到了令人困惑且看似矛盾的规则。考虑下面的例子: >>> text = 'm\n' >>> match = re.search('m\n', text) >>> print match.group() m >>> print text m 令人惊讶的是,上面的代码抛出了一个错误,即使它们都是原始字符串。这意味着两者都只包含文本m\n,没有换行符 >>> text = r'm\n' >>> match = re.search(r'm\\n',

关于原始字符串,我遇到了令人困惑且看似矛盾的规则。考虑下面的例子:

>>> text = 'm\n' >>> match = re.search('m\n', text) >>> print match.group() m >>> print text m 令人惊讶的是,上面的代码抛出了一个错误,即使它们都是原始字符串。这意味着两者都只包含文本
m\n
,没有换行符

>>> text = r'm\n'
>>> match = re.search(r'm\\n', text)
>>> print text
m\n
>>> print match.group()
m\n
令人惊讶的是,上述方法奏效了为什么我必须在搜索中避开反斜杠,而不是文本本身?

然后是没有特殊行为的普通字符的反斜杠:

>>> text = 'm\&'
>>> match = re.search('m\&', text)
>>> print text
m\&
>>> print match.group()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'group'
我对原始字符串的理解是,它们抑制python中反斜杠的行为。对于正则表达式,这一点很重要,因为它允许re.search应用自己的内部反斜杠行为,并防止与Python发生冲突。然而,在上述情况下,反斜杠实际上毫无意义,我不知道为什么它看起来是必要的。更糟糕的是,我不明白为什么我需要反斜杠来表示模式,而不是文本,而且当我将这两个字符串都制作为原始字符串时,它似乎不起作用

在这方面不要提供太多指导。他们关注有明显问题的示例,例如
'\section'
,其中
\s
是元字符。寻找一个完整的答案,以防止像这样的意外行为

text = r'm\n'
match = re.search(r'm\\n', text)
第一行使用
r
阻止python将
\n
解释为单字节

使用
r
的第二行与第一行的作用相同。使用
\
可以防止正则表达式解释为
\n
。正则表达式还使用
\
类似的
\s
\d

以下字符是赋予正则表达式搜索语法特殊含义的元字符:

\反斜杠转义字符。 反斜杠赋予它后面的字符特殊的含义。例如,组合“\n”表示控制字符之一的换行符。组合“\w”表示“word”字符,这是一种方便的转义序列,“\1”是一种替换特殊字符。 示例:正则表达式“aa\n”尝试在一行末尾匹配两个连续的“a”,包括换行符本身。 示例:“a+”与“a+”匹配,而不是一系列的“a”或“a”


为了理解您所困惑的字符串的内部表示。我建议您使用和内置函数。使用这些工具,您将能够准确地理解字符串是如何工作的,并且不再对模式匹配感到困惑,因为您将准确地了解内部表示。例如,假设您想分析遇到问题的字符串:

use_cases = [
    'm\n',
    r'm\n',
    'm\\n',
    r'm\\n',
    'm\&',
    r'm\&',
    'm\\&',
    r'm\\&',
]

for u in use_cases:
    print('-' * 10)
    print(u, repr(u), len(u))
产出将是:

----------
m
 'm\n' 2
----------
m\n 'm\\n' 3
----------
m\n 'm\\n' 3
----------
m\\n 'm\\\\n' 4
----------
m\& 'm\\&' 3
----------
m\& 'm\\&' 3
----------
m\& 'm\\&' 3
----------
m\\& 'm\\\\&' 4

因此,您可以准确地看到普通字符串与原始字符串之间的差异。

在常规Python字符串中,
'm\n'
\n
表示一个换行符,而在原始字符串
r'm\n'
中,
\
n
只是它们本身。到目前为止,一切都很简单

如果将字符串
'm\n'
作为模式传递给
re.search()
,则传递的是一个两个字符的字符串(
m
,后跟换行符),并且
re
将很高兴地为您查找这两个字符字符串的实例

如果传递三个字符的字符串
r'm\n'
re
模块本身将把这两个字符
\
n
解释为具有特殊含义“匹配换行符”,因此整个模式意味着“匹配一个
m
,然后是一个换行符”,就像前面一样

在第三个示例中,由于字符串
r'm\n'
不包含换行符,因此没有匹配项:

>>> text = r'm\n'
>>> match = re.search(r'm\n', text)
>>> print(match)
None
使用模式
r'm\\n'
,您将两个实际的反斜杠传递给
re.search()
,并且模块本身
re
将双反斜杠解释为“匹配单个反斜杠字符”

'm\&'
的例子中,发生了一些稍微不同的事情。Python将反斜杠视为常规字符,因为它不是转义序列的一部分<另一方面,code>re,简单地丢弃
\
,因此模式实际上是
m&
。通过对
'm&'
测试模式,可以看出这是正确的:

>>> re.search('m\&', 'm&').group()
'm&'
与前面一样,将反斜杠加倍告诉
re
搜索实际的反斜杠字符:

>>> re.search(r'm\\&', 'm\&').group()
'm\\&'
。。。为了让事情变得更加混乱,用Python double表示单个反斜杠。通过打印它,您可以看到它实际上是一个反斜杠:

>>> print(re.search(r'm\\&', 'm\&').group())
m\&

简单地说,
\
在正则表达式中有一个特殊的含义。例如,空格字符为
\s
,十进制数字为
\d
,新行字符为
\n
,等等

当您将字符串定义为

s = 'foo\n'
此字符串包含字符
f
o
o
和新行字符(长度4)

但是,在定义原始字符串时:

s = r'foo\n'
此字符串包含字符
f
o
o
\
n
(长度5)

当您使用原始
\n
(即
r'\n'
)编译regexp时,它将匹配所有新行。类似地,只要使用新行字符(即
'\n'
),它将匹配新行字符,就像
a
匹配
a
一样

一旦你理解了这个概念,你就应该能够理解剩下的


再详细说明一下。为了使用正则表达式匹配反斜杠字符
\
,有效的正则表达式是
\
,在Python中是
r'\\'
或其等价的
'\\\\'
,这一点也不奇怪
r'm\n
属于
>>> print(re.search(r'm\\&', 'm\&').group())
m\&
s = 'foo\n'
s = r'foo\n'