Python正则表达式非贪婪匹配

3
这个问题来自于《用Python自动化》一书。
 atRegex1 = re.compile(r'\w{1,2}at')
 atRegex2 = re.compile(r'\w{1,2}?at')

 atRegex1.findall('The cat in the hat sat on the flat mat.')
 atRegex2.findall('The cat in the hat sat on the flat mat.')

我认为问号?应该进行非贪婪匹配,所以\w{1,2}?只返回一个字符。但是对于这两个函数,我得到了相同的输出:

['cat', 'hat', 'sat', 'flat', 'mat']

在这本书中,

nongreedyHaRegex = re.compile(r'(Ha){3,5}?')
mo2 = nongreedyHaRegex.search('HaHaHaHaHa')
mo2.group()
'HaHaHa'

有人能帮我理解为什么会有差异吗?谢谢!

懒惰量词仅在字符串向前运行时起作用。它将尝试在给定位置的每个尝试,并在需要时进行回溯。它匹配“flat”,因为在“flat”中的“f”位置上,它仍然与您的正则表达式匹配:它不会向前跟踪,而是会回溯。它不会从“flat”中匹配“lat”,因为它已经使用了这些字符。 - ctwheels
由于下面已经解释了一切,我只能建议使用 \b\wat\b 或更精确的 \b[^\W\d_]at\b 模式,如果你只想匹配三个字母的完整单词。请记住,正则表达式中懒惰量化模式的最后一部分总是尽可能少地匹配符号。所以 as*? 只会匹配 a,而 as{1,200}? 总是匹配 as - Wiktor Stribiżew
2个回答

1
第二个正则表达式有一个已知的匹配模式:Ha最少匹配3次,最多匹配5次,但尽可能少。因此,在这种情况下,它永远不会超过3次,与(Ha){3}相同。引擎会尽快满足条件。 (Ha){3,5}?的匹配结果与以下结果相同(将组视为一个整体):
(Ha){3}|(Ha){4}|(Ha){5}

(Ha){3,5}与以下表达式匹配结果相同:

(Ha){5}|(Ha){4}|(Ha){3}

因此,如果在两个正则表达式的第一个交替部分中找到了匹配项,则引擎不会尝试新的匹配。

那么 \w{1,2}?at 呢?让我们来翻译一下:

(?:\w{1}|\w{2})at

在交替表达式的第一个侧优先匹配成功后即结束匹配。对于 \w{1,2}at 也是如此:

(?:\w{2}|\w{1})at

注意:如果第一面不匹配,引擎将按顺序选择其他面。

1
你所遇到的问题是由于正则表达式中回溯的特性所导致的。正则表达式引擎在每个给定的位置解析字符串,因此会尝试模式的每个选项,直到它在该位置匹配或失败为止。如果匹配成功,它将消耗那些字符,如果失败,则继续到下一个位置,直到达到字符串的结尾。
关键词是“回溯”。我认为 Microsoft文档很好地定义了这个术语(我已经加粗了重要部分)。
回溯是指正则表达式模式包含可选定量词或选择构造,并且正则表达式引擎返回到先前保存的状态以继续搜索匹配。回溯是正则表达式强大和灵活的核心,使其能够匹配非常复杂的模式。同时,这种强大功能也带来了代价。回溯通常是影响正则表达式引擎性能的最重要因素。幸运的是,开发人员可以控制正则表达式引擎的行为以及它如何使用回溯。本主题解释了回溯的工作原理以及如何控制它。
正则表达式引擎会回溯到先前保存的状态。它不能向前跟踪到未来保存的状态,尽管这将非常方便!由于您指定匹配应以“at”结尾(惰性定量词在其之前),因此它将耗尽每个正则表达式选项,直到以“at”结尾的\w {1,2} 成功匹配。
那么,如何避免这种情况呢?嗯,最简单的方法可能是使用捕获组:

在此处查看正则表达式的使用

\w*(\w{1,2}?at)
\w*(\w{1,2}at)    # yields same results as above (but in more steps)
\w*(\wat)         # yields same results as above (faster method)
\wat              # yields same results as above (fastest method)
\b\w{1,2}at\b     # perhaps this is what OP is after?
  • \w*匹配任意数量的单词字符。这是为了让我们能够模拟前向跟踪(这不是一个正式术语,只是在上面的回答的上下文中使用)。它将尽可能匹配多的字符,并向后工作,直到匹配成功。
  • OP已经有了其他模式。实际上,\w{2}永远不会被满足,因为\w只会被匹配一次(由于 \w* 是贪婪的),因此可以使用\wat而不是\w*(\wat)。也许OP打算在正则表达式中使用锚点,例如\b\b\w{1,2}at\b?这与原始的OP的正则表达式没有区别,因为使量词懒惰理论上在前向跟踪的上下文中将产生相同的结果(一个\w的匹配将满足\w{1,2}?, 因此永远不会达到\w{2})。

非常感谢您详细的回答!现在我明白它是如何工作的。 - dyxdyx

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