获取所有匹配项,包括重叠项

时间:2018-08-05 18:34:43

标签: python regex

我想以这样一种方式解析text:将带有数字的方括号前后添加到子字符串中。据我了解,正则表达式通常会消耗字符串,这意味着默认情况下不会有匹配项重叠,对吗?我该如何调整pattern_3才能获得所需的输出?

import re

text = 'a(1)a(2)a(1)a'
pattern = '(a(?:\((\d+)\))?)'
re.findall(pattern, text)
>>> [('a(1)', '1'), ('a(2)', '2'), ('a(1)', '1'), ('a', '')]


pattern_2 = '((?:\((\d+)\))?a(?:\((\d+)\))?)'
re.findall(pattern_2, text)
>>> [('a(1)', '', '1'), ('a(2)', '', '2'), ('a(1)', '', '1'), ('a', '', '')]


pattern_3 = pattern = '((?:\((\d+)\))?a(?=(?:\((\d+)\)))?)'
re.findall(pattern_3, text)
>>> [('a', '', '1'), ('(1)a', '1', '2'), ('(2)a', '2', '1'), ('(1)a', '1', '')]


# desired output:
>>> [('a(1)', '', '1'), ('(1)a(2)', '1', '2'), ('(2)a(1)', '2', '1'), ('(1)a', '1', '')]

更新

仅使用re寻找解决方案

4 个答案:

答案 0 :(得分:1)

您可以尝试使用此模式(?=(\(\d+?\)[a-z]\(\d+?\)|[a-z]\(\d+?\)|\(\d+?\)[a-z])),该模式可以使用正向超前解决问题。

由于环顾四周是断言,因此它们是匹配的,但不消耗字符串,因此将捕获组放入其中就足够了。然后,您可以多次匹配字符串的相同部分,并通过捕获组访问这些匹配项。

在我的解决方案中,总是有一个捕获组。

请参阅以下内容以供参考:How to find overlapping matches with a regexp?

Demo

答案 1 :(得分:1)

您可以使用:

re.findall(r'(?=\(\d+\)a|a\(\d+\))(?=((?:\((\d+)\))?a(?:\((\d+)\))?)).*?a', s)

说明:

第一个前行检查a括号之间是否至少有一个数字。

这里的第二个前瞻仅捕获您想要的内容,但是由于两个\(\d+\)是可选的,因此需要第一个前瞻。

然后,您只需要消耗字符,直到a.*?a一起使用,就可以避免两次与同一个a匹配。

demo

答案 2 :(得分:1)

要获得重叠的匹配,使用在超前区域内捕获组是正确的主意。

首先定义起点(零宽度)。它应该在字符串的开头,或者在左括号之前:(?:^|(?=\())。因为我们只需要a( ...开头或( ...内或结尾即可。

这时触发超前。通过将每个部分设为可选,并在其中第二个组来提取数字,用于在前瞻(?= ... )内部捕获的模式可能类似于((?:\((\d+)\))?a?(?:\((\d+)\))?)。也可以通过交替使用不同的选项来实现。

(?:^|(?=\())(?=((?:\((\d+)\))?a?(?:\((\d+)\))?))

Here is a demo at regex101

答案 3 :(得分:0)

省略括号以使解决方案更整洁,但是您可以放回它们:

text='a1a2a1a'

您必须从结果中过滤出空字符串:

re.findall(r"(?=^(a(\d)))|(?=((\d)a)$)|(?=((\d)a(\d)))",text)
     Out:
    [('a1', '1', '', '', '', '', ''),
     ('', '', '', '', '1a2', '1', '2'),
     ('', '', '', '', '2a1', '2', '1'),
     ('', '', '1a', '1', '', '', '')]

编辑: 根据@MichałTurczyn的说法,一个前瞻也可以做到:

re.findall(r"(?=^(a(\d))|((\d)a)$|((\d)a(\d)))",text)