你能解释一下为什么这个正则表达式不起作用吗?

3
>>> d = "Batman,Superman"
>>> m = re.search("(?<!Bat)\w+",d)
>>> m.group(0)
'Batman'

为什么group(0)没有匹配到Superman?这篇反向引用教程解释道:
(?

这是一个非常出色的正则表达式测试工具,它在许多场合下都帮了我大忙。 - chisaipete
5个回答

6

Batman并不是直接跟在Bat后面的,因此首先匹配的是它。实际上,Superman也不是;在您的字符串中有一个逗号,这足以允许RE匹配,但它仍然没有匹配,因为可能在字符串中更早地匹配。

也许这样会更好地解释:如果字符串是Batman,并且您从m开始尝试匹配,那么RE将不会匹配,直到字符之后(给出匹配an),因为这是字符串中唯一一个前面跟着Bat的位置。


1

1
在简单的层面上,正则表达式引擎从字符串的左侧开始,并向右逐步移动,尝试匹配您的模式(可以将其视为光标在字符串中移动)。在查找环节的情况下,在光标停止的每个位置上,都会断言查找环节,如果为真,则引擎继续尝试进行匹配。一旦引擎能够匹配您的模式,它就会返回一个匹配项。
在您字符串的位置0(即在“Batman”中的B之前),断言成功,因为当前位置之前不存在“Bat”,因此\w+可以匹配整个单词“Batman”(请记住,正则表达式本质上是“贪心”的 - 即将尽可能多地匹配)。
请参见this page以获取更多有关引擎内部的信息。
为了实现你想要的,你可以使用类似以下的东西:
\b(?!Bat)\w+

在这个模式中,引擎将匹配一个单词边界\b1,然后是一个或多个单词字符,并断言这些单词字符不以Bat开头。使用了前瞻而不是后顾,因为在这里使用后顾会和您原来的模式有同样的问题;它会在紧跟在单词边界之后的位置之前查找,而且由于已经确定光标之前的位置是单词边界,否定的后顾会始终成功。

1注意,单词边界匹配\w\W之间的边界(即[A-Za-z0-9_]与任何其他字符之间的边界;它还匹配^$锚点)。如果您的边界需要更复杂的情况,您将需要一种不同的方式对模式进行定位。


1

你正在寻找第一组一个或多个字母数字字符(\w+),它不是由'Bat'前置。Batman是第一个这样的匹配项。(请注意,负回顾断言可以匹配字符串的开头。)


1
要实现您想要的功能,您必须将正则表达式限制为仅匹配'man';否则,就像其他人指出的那样,\w会贪婪地匹配任何内容,包括'Batman'。代码如下:
>>> re.search("\w+(?<!Bat)man","Batman,Superman").group(0)
'Superman'

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接