重新计算重叠的正则匹配

7 投票
2 回答
615 浏览
提问于 2025-04-17 13:10

我想知道如何用Python获取重叠正则表达式匹配的数量。

我看过并尝试了这个那个以及其他一些问题的建议,但都没有适合我情况的解决方案。我的情况是这样的:

  • 输入示例字符串:akka
  • 搜索模式:a.*k

一个合适的函数应该返回2作为匹配的数量,因为有两个可能的结束位置(k字母)。

这个模式可能会更复杂,比如a.*k.*aakka中也应该匹配两次(因为中间有两个k)。

2 个回答

3

我觉得你想要的功能可能用一个叫做 lepl 的解析库来实现会更好:

>>> from lepl import *
>>> parser = Literal('a') + Any()[:] + Literal('k')
>>> parser.config.no_full_first_match()
>>> list(parser.parse_all('akka'))
[['akk'], ['ak']]
>>> parser = Literal('a') + Any()[:] + Literal('k') + Any()[:] + Literal('a')
>>> list(parser.parse_all('akka'))
[['akka'], ['akka']]

我认为你需要的就是 parser.parse_all 输出的长度。

注意,如果你的模式没有匹配整个字符串,你需要使用 parser.config.no_full_first_match() 来避免出现错误。

补充一下:根据 @Shamanu4 的评论,我明白你想从任意位置开始匹配结果,你可以这样做:

>>> text = 'bboo'
>>> parser = Literal('b') + Any()[:] + Literal('o')
>>> parser.config.no_full_first_match()
>>> substrings = [text[i:] for i in range(len(text))]
>>> matches = [list(parser.parse_all(substring)) for substring in substrings]
>>> matches = filter(None, matches) # Remove empty matches
>>> matches = list(itertools.chain.from_iterable(matches)) # Flatten results
>>> matches = list(itertools.chain.from_iterable(matches)) # Flatten results (again)
>>> matches
['bboo', 'bbo', 'boo', 'bo']
2

是的,这个代码看起来很丑,也没有经过优化,但它似乎可以正常工作。这只是一个简单的尝试,去测试所有可能的但唯一的变体。

def myregex(pattern,text,dir=0):
    import re
    m = re.search(pattern, text)
    if m:
        yield m.group(0)
        if len(m.group('suffix')):
            for r in myregex(pattern, "%s%s%s" % (m.group('prefix'),m.group('suffix')[1:],m.group('end')),1):
                yield r
            if dir<1 :
                for r in myregex(pattern, "%s%s%s" % (m.group('prefix'),m.group('suffix')[:-1],m.group('end')),-1):
                    yield r


def myprocess(pattern, text):    
    parts = pattern.split("*")    
    for i in range(0, len(parts)-1 ):
        res=""
        for j in range(0, len(parts) ):
            if j==0:
                res+="(?P<prefix>"
            if j==i:
                res+=")(?P<suffix>"
            res+=parts[j]
            if j==i+1:
                res+=")(?P<end>"
            if j<len(parts)-1:
                if j==i:
                    res+=".*"
                else:
                    res+=".*?"
            else:
                res+=")"
        for r in myregex(res,text):
            yield r

def mycount(pattern, text):
    return set(myprocess(pattern, text))

测试:

>>> mycount('a*b*c','abc')
set(['abc'])
>>> mycount('a*k','akka')
set(['akk', 'ak'])
>>> mycount('b*o','bboo')
set(['bbo', 'bboo', 'bo', 'boo'])
>>> mycount('b*o','bb123oo')
set(['b123o', 'bb123oo', 'bb123o', 'b123oo'])
>>> mycount('b*o','ffbfbfffofoff')
set(['bfbfffofo', 'bfbfffo', 'bfffofo', 'bfffo'])

撰写回答