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(),因为我的模式的开头从 fro 开始,(?=所以它不消耗任何字符串(我需要它来捕获重叠匹配)。显然pat.findall提取了所需的字符串,但是如何获取开始和停止索引?

4

2 回答 2

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) 捕获组的开始和结束。通过不传递参数,您将获得整个匹配的开始和结束 - 但当然匹配的断言总是匹配空字符串(因此开始和结束是相等的)。

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

编辑:经过一夜的睡眠和对 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()返回)。

于 2013-11-15T05:16:07.250 回答
4

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

要么匹配,要么不匹配。您匹配的任何内容都只能是一场比赛/子比赛的一部分。

前瞻是短暂的,它们不会增加任何实际计数器。

于 2013-11-15T05:03:38.630 回答