获取重叠匹配的起始和结束索引?

6

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

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 提取了所需的字符串,但如何获取起始和结束索引?

抱歉,我误解了您的问题——一开始我不清楚您所说的重叠匹配是什么意思。但是,有时候人们在真诚地尝试帮助时会犯错误——鉴于此,我认为您的态度是不必要的。向某人礼貌地解释他们可能误解了您并不难——大多数人在被礼貌地告知后都会感到道歉并寻求正确的方法——有时甚至会比没有误解时更加努力地去做,出于弥补自己的错误的愿望。通过粗鲁的行为,您放弃了所有的考虑。 - Andrew Cheong
抱歉如果我有失礼。我很感激你想要帮助的愿望。 - ashim
我看到你删掉了其他的评论——也许是我误判了你——很抱歉在发帖前没有花更多时间理解你的问题。 - Andrew Cheong
2个回答

阿里云服务器只需要99元/年,新老用户同享,点击查看详情
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代码进行两个字符的更改:

print it.start(1)
print it.end(1)
也就是说,获取第一个(1)捕获组的开始和结束。如果不传递参数,您将得到整个匹配的开始和结束-但是,匹配断言始终匹配空字符串(因此开始和结束相等),这是一种危险的做法,因为断言内部的捕获组(无论是正向预查还是反向预查、正面还是负面等)的语义最多也是模糊的。很难说您是否可能会意外发现错误(或实现问题)!这很可爱。编辑:经过一夜的睡眠和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()返回的匹配对象那样挑选和选择)。


@Tim Peters 有趣的建议,只需要更改两个字符。我自己也考虑过这个问题,试图添加0,因为我认为我正在匹配整个匹配!所以在这种情况下,整个组为空,但子组不为空 :-) - ashim
@msh,没错!这正是我犹豫依赖它的原因:在空匹配中存在非空匹配的概念只会让人想到“错误警报!”;-) - Tim Peters
@Tim看起来我在理论上是对的,但在实践中却是错的。.NET正则表达式做了同样的事情 - 即使匹配的总长度为0,您也可以提取断言内组的长度。 - Tomalak
@Tomalak,我们在同一艘船上;-) 现在正试图在 Python-Dev 邮件列表上讨论它。 - Tim Peters

4

正则表达式中不存在重叠匹配。

要么匹配,要么不匹配。任何匹配的内容只能是一个匹配/子匹配的一部分。

预先查看是短暂的,它们不会增加任何实际的计数器。


你在 finditer() 中的 .start().end() 索引返回相同的数字,因为正则表达式中没有重叠的匹配项。但是,你可以相信其他的事情。 - Tomalak
如果您编辑了您的回答,我可以取消踩,否则踩将被锁定。 - ashim
不,我不会这样做。也许下次在投反对票之前你可以多花点时间考虑。 - Tomalak

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