匹配多行文本块的正则表达式

当与跨多行的文本进行匹配时,使用 Python 正则表达式有一点麻烦。示例文本是(‘ n’是换行符)

some Varying TEXT\n
\n
DSJFKDAFJKDAFJDSAKFJADSFLKDLAFKDSAF\n
[more of the above, ending with a newline]\n
[yep, there is a variable number of lines here]\n
\n
(repeat the above a few hundred times).

我想捕捉两个东西: ‘ some _ Varying _ TEXT’部分,以及在一个捕捉中大写文本下面两行的所有行(稍后我可以去掉换行符)。 我试过几种方法:

re.compile(r"^>(\w+)$$([.$]+)^$", re.MULTILINE) # try to capture both parts
re.compile(r"(^[^>][\w\s]+)$", re.MULTILINE|re.DOTALL) # just textlines

还有这里的很多变种,但都没有运气。最后一个似乎一行一行地匹配文本,这不是我真正想要的。我可以捕捉第一部分,没有问题,但我似乎不能捕捉4-5行的大写文本。 我希望 match.group (1)是一些变化的文本,group (2)是 line1 + line2 + line3 + 等,直到遇到空行。

如果有人好奇的话,它应该是组成蛋白质的氨基酸序列。

350260 次浏览

查找:

^>([^\n\r]+)[\n\r]([A-Z\n\r]+)

1 = some _ variable _ text

2 = 所有 CAPS 的行

编辑(证明这种方法有效) :

text = """> some_Varying_TEXT


DSJFKDAFJKDAFJDSAKFJADSFLKDLAFKDSAF
GATACAACATAGGATACA
GGGGGAAAAAAAATTTTTTTTT
CCCCAAAA


> some_Varying_TEXT2


DJASDFHKJFHKSDHF
HHASGDFTERYTERE
GAGAGAGAGAG
PPPPPAAAAAAAAAAAAAAAP
"""


import re


regex = re.compile(r'^>([^\n\r]+)[\n\r]([A-Z\n\r]+)', re.MULTILINE)
matches = [m.groups() for m in regex.finditer(text)]
#NOTE can be sorter with matches = re.findall(pattern, text, re.MULTILINE)


for m in matches:
print 'Name: %s\nSequence:%s' % (m[0], m[1])

这将奏效:

>>> import re
>>> rx_sequence=re.compile(r"^(.+?)\n\n((?:[A-Z]+\n)+)",re.MULTILINE)
>>> rx_blanks=re.compile(r"\W+") # to remove blanks and newlines
>>> text="""Some varying text1
...
... AAABBBBBBCCCCCCDDDDDDD
... EEEEEEEFFFFFFFFGGGGGGG
... HHHHHHIIIIIJJJJJJJKKKK
...
... Some varying text 2
...
... LLLLLMMMMMMNNNNNNNOOOO
... PPPPPPPQQQQQQRRRRRRSSS
... TTTTTUUUUUVVVVVVWWWWWW
... """
>>> for match in rx_sequence.finditer(text):
...   title, sequence = match.groups()
...   title = title.strip()
...   sequence = rx_blanks.sub("",sequence)
...   print "Title:",title
...   print "Sequence:",sequence
...   print
...
Title: Some varying text1
Sequence: AAABBBBBBCCCCCCDDDDDDDEEEEEEEFFFFFFFFGGGGGGGHHHHHHIIIIIJJJJJJJKKKK


Title: Some varying text 2
Sequence: LLLLLMMMMMMNNNNNNNOOOOPPPPPPPQQQQQQRRRRRRSSSTTTTTUUUUUVVVVVVWWWWWW

关于这个正则表达式的一些解释可能会很有用: ^(.+?)\n\n((?:[A-Z]+\n)+)

  • 第一个字符(^)表示“从一行的开头开始”。请注意,它与换行本身不匹配(与 $相同: 它的意思是“就在换行之前”,但它与换行本身不匹配)。
  • 然后 (.+?)\n\n表示“匹配尽可能少的字符(所有字符都允许) ,直到达到两个换行”。结果(不带换行符)放在第一组中。
  • [A-Z]+\n的意思是“匹配尽可能多的大写字母,直到你到达一个换行。”。这定义了我将称之为 短信的内容。
  • ((?:短信)+)意思是匹配一个或多个 短信,但不要把每一行放在一组中。相反,将 所有短信放在一组中。
  • 如果希望在结尾强制使用双换行符,可以在正则表达式中添加最终的 \n
  • 此外,如果您不确定您将获得哪种类型的换行(\n\r\r\n) ,那么只需将每次出现的 \n替换为 (?:\n|\r\n?)来修复正则表达式。

试试这个:

re.compile(r"^(.+)\n((?:\n.+)+)", re.MULTILINE)

我认为你最大的问题是,你期望 ^$锚匹配行馈线,但他们没有。在多行模式下,^立即匹配位置 跟随换行,$立即匹配位置 前言换行。

还要注意,换行符可以包含换行符(\n)、回车符(\r)或回车符 + 换行符(\r\n)。如果你不确定你的目标文本只使用了 linefeeds,你应该使用这个更具包容性的正则表达式:

re.compile(r"^(.+)(?:\n|\r\n?)((?:(?:\n|\r\n?).+)+)", re.MULTILINE)

顺便说一句,你不想在这里使用 DOTALL 修饰符; 你依赖的事实是,点匹配的一切 除了换行。

我的偏好。

lineIter= iter(aFile)
for line in lineIter:
if line.startswith( ">" ):
someVaryingText= line
break
assert len( lineIter.next().strip() ) == 0
acids= []
for line in lineIter:
if len(line.strip()) == 0:
break
acids.append( line )

此时,有一些 VaryingText 作为字符串,酸作为字符串列表。 您可以执行 "".join( acids )来创建单个字符串。

我发现这比多行正则表达式更灵活。

如果每个文件只有一个氨基酸序列,我根本不会使用正则表达式。就像这样:

def read_amino_acid_sequence(path):
with open(path) as sequence_file:
title = sequence_file.readline() # read 1st line
aminoacid_sequence = sequence_file.read() # read the rest


# some cleanup, if necessary
title = title.strip() # remove trailing white spaces and newline
aminoacid_sequence = aminoacid_sequence.replace(" ","").replace("\n","")
return title, aminoacid_sequence

下面是匹配多行文本块的正则表达式:

import re
result = re.findall('(startText)(.+)((?:\n.+)+)(endText)',input)

有时候直接在字符串中指定标志比较合适,比如 插入标志:

"(?m)^A complete line$".

例如在单元测试中,使用 正则表达式。这样,在调用断言之前就不需要 import re或者编译正则表达式。