匹配字符串中不是特定字符的连续片段的部分

Match parts of string that are not consecutive stretches of certain character

我有一个简单的函数,它从一个字符串中生成至少有 gapSize 个连续 N 的所有片段:

def get_gap_coordinates(sequence, gapSize=25):
    gapPattern = "N{"+str(gapSize)+",}"
    p = re.compile(gapPattern)
    m = p.finditer(sequence)
    for gap in m:
        start,stop = gap.span()
        yield(start,stop)

现在我想要一个功能正好相反:匹配所有不是至少 gapSize N 的连续段的字符。这些延伸可能出现在字符串中的任何位置(开头、中间和结尾),具有任何给定的数字。

我调查了环顾四周并尝试了

(?!N{25,}).*

但这不能满足我的需要。 非常感谢任何帮助!

编辑: 例如:一个序列 NNNNNNACTGACGTNNNACTGACNNNNNN 应该匹配 ACTGACGTNNNACTGAC gapSize=5 和 ACTGACGT & ACTGAC gapSize = 3。

我想你可以这样做:

gapPattern = "(N{"+str(gapSize)+",})"
p = re.compile(gapPattern)
i = 0
for s in re.split(p, sequence):
    if not re.match(p, s):
        yield i
    i += len(s)

根据 re.split 函数,这将为不是 gap_size "N" 字符的子字符串生成一系列偏移量。

我想过正则表达式直接匹配想要的块,但没有想到什么好办法。我认为最好继续寻找间隙并简单地使用间隙坐标来获得好的块坐标。我的意思是,它们基本上是一样的,对吧?间隙停止是块开始,间隙开始是块停止。

def get_block_coordinates(sequence, gapSize=25):
    gapPattern = "N{"+str(gapSize)+",}"
    p = re.compile(gapPattern)
    m = p.finditer(sequence)
    prevStop = 0
    for gap in m:
        start,stop = gap.span()
        if start:
            yield(prevStop,start)
        prevStop = stop
    if prevStop < len(sequence):
        yield(prevStop,len(sequence))

所以这是一个 pure 正则表达式解决方案,它似乎是你想要的,但我想知道是否真的有更好的方法来做到这一点。当我想到它们时,我会添加替代方案。我使用了几个在线正则表达式工具并在 shell.

中玩了玩

One of the tools 有一个很好的正则表达式图形和生成 SO 答案的工具 代码:正则表达式(间隙为 10)是:

^.*?(?=N{10})|(?<=N{10})[^N].*?(?=N{10})|(?<=N{10})[^N].*?$

用法:

s = 'NAANANNNNNNNNNNBBBBNNNCCNNNNNNNNNNDDDDN'
def foo(s, gapSize = 25):
    '''yields non-gap items (re.match objects) in s or
       if gaps are not present raises StopIteration immediately
    '''
    # beginning of string and followed by a 'gap' OR
    # preceded a 'gap' and followed by a 'gap' OR
    # preceded a 'gap' and followed by end of string
    pattern = r'^.*?(?=N{{{}}})|(?<=N{{{}}})[^N].*?(?=N{{{}}})|(?<=N{{{}}})[^N].*?$'
    pattern = pattern.format(gapSize, gapSize, gapSize, gapSize)
    for match in re.finditer(pattern, s):
        #yield match.span()
        yield match

for match in foo(s, 10):
    print match.span(), match.group()

'''
>>> 
(0, 5) NAANA
(15, 24) BBBBNNNCC
(34, 39) DDDDN
>>>
'''

因此,如果您仔细考虑一下,您会发现 gap 的开始是 non-gap 的结束,并且反之亦然。因此,使用一个简单的正则表达式:迭代间隙,向循环添加逻辑以跟踪 非间隙 跨度和 yield 跨度。 (我的占位符变量名可能会得到改进)

s = 'NAANANNNNNNNNNNBBBBNNNCCNNNNNNNNNNDDDDN'
def bar(s, n):
    '''Yields the span of non-gap items in s or
       immediately raises StopIteration if gaps are not present.
    '''
    gap = r'N{{{},}}'.format(n)
    # initialize the placeholders
    previous_start = 0
    end = len(s)
    for match in re.finditer(gap, s):
        start, end = match.span()
        if start == 0:
            previous_start = end
            continue
        end = start
        yield previous_start, end
        previous_start = match.end()
    if end != len(s):
        yield previous_start, len(s)

用法

for start, end in bar(s, 4):
    print (start, end), s[start:end]

'''
>>>
(0, 5) NAANA
(15, 24) BBBBNNNCC
(34, 39) DDDDN
>>>
''' 

否定前瞻似乎工作正常。例如。对于间隙大小 3,正则表达式为:

N{3,}?([^N](?:(?!N{3,}?).)*) 

试试看 here.

import re

def get_gap_coordinates(sequence, gapSize=25):
    gapPattern = "N{%s,}?([^N](?:(?!N{%s,}?).)*)" % (gapSize, gapSize)
    p = re.compile(gapPattern)
    m = p.finditer(sequence)
    for gap in m:
        start,stop = gap.start(1), gap.end(1)
        yield(start,stop)


for x in get_gap_coordinates('NNNNNNACTGACGTNNNACTGACNNNNN', 3):
    print x

警告:如果字符串不是以 'N' 序列开头,则这可能无法很好地匹配字符串的开头。但是你总是可以从左边用间隙大小倍数 'N' 填充字符串。