如何使用正则表达式找到所有重叠的匹配

157
我正在尝试使用Python 2.6中的re模块,在一系列较大的数字中找到每个10位数的数字序列。
我可以轻松地获取不重叠的匹配,但我想要在数字序列中获取每个匹配项。例如,
在"123456789123456789"中,
我应该得到以下列表:
[1234567891,2345678912,3456789123,4567891234,5678912345,6789123456,7891234567,8912345678,9123456789]

我找到了关于“lookahead”的参考资料,但我看到的例子只显示了数字对,而不是更大的组合,我无法将它们转换为超过两位数。

6
当重叠匹配从同一点开始时,比如在"abcd"中匹配"a|ab|abc",所提供的解决方案无法奏效,只会返回一个结果。有没有不需要多次调用match()并手动跟踪“end”界限的解决方案? - Vítor De Araújo
1
@VítorDeAraújo:重叠的正则表达式,如(a|ab|abc)通常可以重写为非重叠的嵌套捕获组,例如(a(b(c)?)?)?,在解包匹配时我们忽略除最外层(即最左边)捕获组以外的所有内容;不可否认,这样做会稍微有些麻烦且不太易读。但这也将是一个更高效的正则表达式匹配方式。 - smci
5个回答

263

在先行断言中使用捕获组。先行断言会捕获你感兴趣的文本,但实际匹配的是先行断言前面零宽度的子字符串,因此匹配实际上是非重叠的:

import re 
s = "123456789123456789"
matches = re.finditer(r'(?=(\d{10}))', s)
results = [int(match.group(1)) for match in matches]
# results: 
# [1234567891,
#  2345678912,
#  3456789123,
#  4567891234,
#  5678912345,
#  6789123456,
#  7891234567,
#  8912345678,
#  9123456789]

4
我的答案至少比这个快两倍。但这个解决方案有些棘手,我投了赞成票。 - eyquem
27
解释=它不是搜索模式(10个数字),而是搜索在其后出现的任何东西。因此,它找到字符串的位置0、字符串的位置1等等。然后它抓取组(1)-匹配模式并制作这些模式的列表。非常酷。 - Tal Weiss
1
我从未想过你可以在预查中使用匹配组,而这些组通常不应包含在匹配中(实际上匹配的子组并没有出现在完整的匹配中)。由于这种技术在Python 3.4中仍然有效,我猜它被认为是一种特性而不是一个错误。 - JAB
20
我加入了StackOverflow,回答问题并提高了声望,只是为了能够点赞这个回答。目前我被迫使用Python 2.4,因此无法使用Python 3更高级的正则表达式函数,而这正是我正在寻找的奇技淫巧。 - TheSoundDefense
2
你能否在代码中添加更多的解释呢?根据 Stack Overflow 的标准,仅仅在答案中提供代码并不是最好的方式。这样做肯定会帮助到更多的人。 - Akshay Hazari
显示剩余2条评论

102

您还可以尝试使用第三方regex模块(而不是re),该模块支持重叠匹配。

>>> import regex as re
>>> s = "123456789123456789"
>>> matches = re.findall(r'\d{10}', s, overlapped=True)
>>> for match in matches: print(match)  # print match
...
1234567891
2345678912
3456789123
4567891234
5678912345
6789123456
7891234567
8912345678
9123456789

我得到了一个 TypeError: findall()意外收到关键字参数'overlapped'。 - Carsten
@Carsten:首先需要安装“regex”模块:pip install regex - David C
2
好的,谢谢。如果没有安装正则表达式,我本以为会出现导入错误。 - Carsten

15
我很喜欢正则表达式,但在这里它们不是必需的。
只需要简单地。
s =  "123456789123456789"

n = 10
li = [ s[i:i+n] for i in xrange(len(s)-n+1) ]
print '\n'.join(li)

结果

1234567891
2345678912
3456789123
4567891234
5678912345
6789123456
7891234567
8912345678
9123456789

14
在此情况下,不需要使用正则表达式,因为您正在应用“在更大的数字系列中”的特殊知识,所以您已经知道每个位置 0 <= i < len(s)-n+1 都保证是一个十位数匹配的起点。另外,我认为您的代码可以加速,尝试进行代码高尔夫优化以提高速度会很有趣。 - smci

5
跟随接受的答案,下面的方法目前也有效。
import re
s = "123456789123456789"
matches = re.findall(r'(?=(\d{10}))',s)
results = [int(match) for match in matches]

0

传统方式:

import re


S = '123456789123456789'
result = []
while len(S):
    m = re.search(r'\d{10}', S)
    if m:
        result.append(int(m.group()))
        S = S[m.start() + 1:]
    else:
        break
print(result)

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