【问题标题】:How to use Boolean OR inside a regex如何在正则表达式中使用布尔 OR
【发布时间】:2017-02-03 05:16:33
【问题描述】:

我想使用正则表达式来查找子字符串,后跟可变数量的字符,然后是多个子字符串中的任何一个。

重新发现

"ATGTCAGGTAAGCTTAGGGCTTTAGGATT"

应该给我:

['ATGTCAGGTAA', 'ATGTCAGGTAAGCTTAG', 'ATGTCAGGTAAGCTTAGGGCTTTAG']

我尝试了以下所有方法,但均未成功:

import re
string2 = "ATGTCAGGTAAGCTTAGGGCTTTAGGATT"
re.findall('(ATG.*TAA)|(ATG.*TAG)', string2)
re.findall('ATG.*(TAA|TAG)', string2)
re.findall('ATG.*((TAA)|(TAG))', string2)
re.findall('ATG.*(TAA)|(TAG)', string2)
re.findall('ATG.*(TAA)|ATG.*(TAG)', string2)
re.findall('(ATG.*)(TAA)|(ATG.*)(TAG)', string2)
re.findall('(ATG.*)TAA|(ATG.*)TAG', string2)

我在这里错过了什么?

【问题讨论】:

  • findall:返回字符串中所有非重叠匹配项的列表。
  • 作为旁注,ATG.*(TAA|TAG) 只会匹配最长的字符串。那是因为* 运算符是贪婪的。

标签: python regex parsing boolean-logic


【解决方案1】:

这并不容易,因为 a) 您想要重叠匹配,b) 您想要贪婪和非贪婪以及介于两者之间的所有内容。

只要字符串相当短,你可以检查每个子字符串:

import re
s = "ATGTCAGGTAAGCTTAGGGCTTTAGGATT"
p = re.compile(r'ATG.*TA[GA]$')

for start in range(len(s)-6):  # string is at least 6 letters long
    for end in range(start+6, len(s)):
        if p.match(s, pos=start, endpos=end):
            print(s[start:end])

打印出来:

ATGTCAGGTAA
ATGTCAGGTAAGCTTAG
ATGTCAGGTAAGCTTAGGGCTTTAG

由于您似乎使用 DNA 序列或类似的东西,请务必也查看Biopython

【讨论】:

  • 有趣的方法。时间复杂度是 O(N^3) 对吧?
  • 应该是 O(n^2),除非我遗漏了什么。
  • 单独提取所有子字符串需要 O(N^3) (O(N^2) 个子字符串,每个子字符串复制 O(N) 个字符)。如果有人需要真正使用它,最好重新拼写它:编译正则表达式,并使用它的.match() 方法。它接受posendpos 切片索引,因此您不必创建任何子字符串对象(当然,编译正则表达式的费用只需支付一次)。
  • @TimPeters 我以为我在某处读到编译正则表达式并没有太大帮助,因为解释器通常会编译它?但我会相信你的话。我根据您的建议调整了答案。
  • CPython 维护一个缓存映射最近使用的正则表达式字符串到他们编译的正则表达式对象,但这不是真正的重点 - 真正的重点是 re.match() 不接受切片索引,只有 regexp_object.match() 接受.使用切片索引完全避免了创建 O(N^2) 子字符串对象的开销。
【解决方案2】:

我很喜欢接受的答案 :-) 也就是说,我添加这个是为了获取信息,而不是在寻找积分。

如果您对此有很大的需求,尝试在 O(N^2) 对索引上进行匹配可能很快就会变得难以忍受。一项改进是使用.search() 方法直接“跳跃”到唯一可能获得回报的起始索引。所以下面就是这样做的。

它还使用.fullmatch() 方法,因此您不必人为地更改“自然”正则表达式(例如,在您的示例中,无需在正则表达式中添加尾随$ - 事实上,在以下代码中,这样做将不再按预期工作)。注意.fullmatch()是在Python 3.4中添加的,所以这段代码也需要Python 3!

最后,这是为了概括re 模块的finditer() 函数/方法。虽然您不需要匹配对象(您只需要字符串),但它们更普遍适用,并且返回生成器通常也比返回列表更友好。

所以,不,这并不能完全满足您的需求,而是可以在 Python 3 中更快地得到您想要的东西:

def finditer_overlap(regexp, string):
    start = 0
    n = len(string)
    while start <= n:
        # don't know whether regexp will find shortest or
        # longest match, but _will_ find leftmost match
        m = regexp.search(string, start)
        if m is None:
            return
        start = m.start()
        for finish in range(start, n+1):
            m = regexp.fullmatch(string, start, finish)
            if m is not None:
                yield m
        start += 1

然后,例如,

import re
string2 = "ATGTCAGGTAAGCTTAGGGCTTTAGGATT"
pat = re.compile("ATG.*(TAA|TAG)")
for match in finditer_overlap(pat, string2):
    print(match.group())

在示例中打印您想要的内容。您尝试编写正则表达式的其他方法也应该有效。在这个例子中它更快,因为第二次围绕外循环 start 是 1,而 regexp.search(string, 1) 找不到另一个匹配项,所以生成器立即退出(因此跳过检查 O(N^2) 其他索引对)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-23
    • 1970-01-01
    • 2017-07-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-17
    • 1970-01-01
    相关资源
    最近更新 更多