获取重叠匹配的开始和停止索引?

时间:2013-11-15 04:37:56

标签: python regex

我需要知道下一个正则表达式的匹配的开始和结束索引:

pat = re.compile("(?=(ATG(?:(?!TAA|TGA|TAG)\w\w\w)*))")

示例字符串为s='GATGDTATGDTAAAA'

pat.findall(s)返回所需的匹配['ATGDTATGD', 'ATGDTAAAA']。如何提取开始和结束索引? 我试过了:

iters = pat.finditer(s)
for it in iters:
    print it.start()
    print it.end()

但是,it.end()始终与it.start()重合,因为我的模式的开始从(?=开始,因此它不会消耗任何字符串(我需要它来捕获重叠匹配)。显然pat.findall提取了所需的字符串,但是如何获取开始和停止索引?

2 个答案:

答案 0 :(得分:5)

正如@Tomalak所说,正则表达式引擎没有内置的重叠匹配概念,所以没有找到“聪明”的解决方案(结果证明是错误的 - 见下文)。但是用循环来做这件事很简单:

import re
pat = re.compile("ATG(?:(?!TAA|TGA|TAG)\w\w\w)*")
s = 'GATGDTATGDTAAAA'
i = 0
while True:
    m = pat.search(s, i)
    if m:
        start, end = m.span()
        print "match at {}:{} {!r}".format(start, end, m.group())
        i = start + 1
    else:
        break

显示

match at 1:10 'ATGDTATGD'
match at 6:15 'ATGDTAAAA'

它的工作原理是在最后一场比赛开始之后重新开始搜索一个角色,直到找不到更多的匹配。

“聪明”还是定时炸弹?

如果您想生活危险,可以对原始finditer代码进行2个字符的更改:

print it.start(1)
print it.end(1)

即,获取第一个(1)捕获组的开始和结束。通过不传递参数,你得到整个匹配的开始和结束 - 但是当然匹配的断言总是匹配一个空字符串(因此start和end是相等的)。

我说这是危险的生活,因为一个捕获组里面的语义一个断言(无论是向前看还是向后看,正面还是负面......)都充其量是模糊的。很难说你是否在这里偶然发现了一个错误(或实施意外)!可爱: - )

编辑:经过一夜的睡眠和对Python-Dev的简短讨论,我相信这种行为是有意的(并且也是如此可靠)。要查找正则表达式R的所有(可能重叠!)匹配,请将其包装如下:

pat = re.compile("(?=(" + R + "))")

然后

for m in pat.finditer(some_string):
    m.group(1)  # the matched substring
    m.span(1)   # the slice indices of the match substring
    # etc

工作正常。

最好将(?=(R))称为“匹配空字符串,但仅当R在此处开始时,如果成功,则将有关R匹配的信息放入组1中”。然后finditer()继续进行匹配空字符串时的操作:它将搜索的开始移动到下一个字符,然后再次尝试(与我的第一个答案中的手动循环相同)。

将此与findall()一起使用会比较棘手,因为如果R也包含捕获组,您将获得所有这些(无法选择和选择,因为您可以使用匹配对象例如finditer()返回)。

答案 1 :(得分:4)

正则表达式中没有重叠匹配。

要么你匹配某些东西,要么你不匹配。您匹配的任何内容只能是一个匹配/子匹配的一部分。

前瞻是短暂的,他们不会增加任何真正的反击。