如何使用正则表达式查找重叠的匹配?

104
>>> match = re.findall(r'\w\w', 'hello')
>>> print match
['he', 'll']

\w\w表示两个字符,因此期望的是'he'和'll'。但为什么'el'和'lo'不符合这个正则表达式呢?

>>> match1 = re.findall(r'el', 'hello')
>>> print match1
['el']
>>>

5
"Lookahead" 的意思是 "向前查找",在正则表达式中这是一种非捕获性分组,用于匹配某个位置之后紧跟着指定模式的字符。它可以帮助我们处理重叠的匹配问题,避免出现重复匹配的情况。 - Pavan Manjunath
4个回答

146

findall默认情况下不会产生重叠的匹配结果。但是,这个表达式可以:

>>> re.findall(r'(?=(\w\w))', 'hello')
['he', 'el', 'll', 'lo']

这里的(?=...)是一种前向断言:

(?=...)匹配如果...紧随其后,但不会消耗字符串。这被称为前向断言。例如,Isaac (?=Asimov)只会匹配'Isaac '如果它后面跟着'Asimov'


4
但我不明白为什么如果它在正向预查中,它会继续前进到下一个字母。能否请你解释一下? - MrZH6
1
我猜这是由于组捕获(\w\w周围的大括号)引起的。实际匹配仍然是一个空字符串,而第1组填充了\w\w(您可以在https://regex101.com/上测试)。因此,我认为它在组中捕获它,但不会超过它,因为匹配长度为零。而Python的re.findall将打印捕获的组https://docs.python.org/3/library/re.html#re.findall - Sviatozar Petrenko

53
你可以使用支持重叠匹配的新的 Python 正则表达式模块regex
>>> import regex as re
>>> match = re.findall(r'\w\w', 'hello', overlapped=True)
>>> print match
['he', 'el', 'll', 'lo']

13

除了零长度断言外,输入中的字符始终会在匹配过程中被消耗。 如果您需要捕获输入字符串中特定字符多次出现的情况,您将需要在正则表达式中使用零长度断言。

有几种零长度断言(例如 ^ (输入/行开头), $ (输入/行结尾), \ b (单词边界)),但是环视( (? <=) 正向后查找和 (? =) 正向前查找)是唯一一种可以从输入中捕获重叠文本的方法。负向环视( (? 负向后查找, (?!)负向前查找)在这里不太有用:如果它们为真,则内部捕获失败;如果它们为假,则匹配失败。这些断言是零长度的(如前所述),这意味着它们会在不消耗输入字符串中的字符的情况下进行断言。 如果断言通过,它们实际上将匹配空字符串。

应用上述知识,适用于您情况的正则表达式将是:

(?=(\w\w))

0

虽然我不是正则表达式专家,但我想回答一个类似的问题

如果你想在前瞻中使用捕获组:

示例正则表达式:(\d)(?=.\1)

字符串:5252

这将匹配第一个5和第一个2

(\d)用于创建一个捕获组,(?=\d\1)用于匹配任何数字后跟捕获组1而不消耗字符串,从而允许重叠。


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