簡體   English   中英

匹配多行文本塊的正則表達式

[英]Regular expression matching a multiline block of text

在匹配跨多行的文本時,讓 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) 成為 some_Varying_Text 和 group(2) 成為 line1+line2+line3+etc 直到遇到空行。

如果有人好奇,它應該是構成蛋白質的氨基酸序列。

嘗試這個:

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

我認為您最大的問題是您希望^$錨匹配換行符,但事實並非如此。 在多行模式下, ^匹配緊跟在換行符之后的 position,而$匹配緊接在換行符之前的 position。

還要注意,換行符可以由換行符 ( \n )、回車符 ( \r ) 或回車符+換行符 ( \r\n ) 組成。 如果您不確定您的目標文本是否只使用換行符,您應該使用這個更具包容性的正則表達式版本:

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

順便說一句,您不想在這里使用 DOTALL 修飾符; 您依賴於點匹配換行符之外的所有內容這一事實。

這將起作用:

>>> 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((?:[AZ]+\n)+)

  • 第一個字符 ( ^ ) 表示“從行首開始”。 請注意,它與換行符本身不匹配(對於 $: 它表示“就在換行符之前”,但它與換行符本身不匹配)。
  • 然后(.+?)\n\n表示“匹配盡可能少的字符(允許所有字符),直到達到兩個換行符”。 結果(沒有換行符)放在第一組中。
  • [AZ]+\n的意思是“匹配盡可能多的大寫字母,直到到達換行符。這定義了我將稱之為textline的內容。
  • ((?: textline )+)表示匹配一個或多個文本行,但不要將每一行放在一個組中。 相反,將所有文本行放在一組中。
  • 如果要在末尾強制使用雙換行符,可以在正則表達式中添加最后一個\n
  • 此外,如果您不確定您將獲得哪種類型的換行符( \n\r\r\n ),那么只需將\n的每次出現替換為(?:\n|\r\n?)

以下是匹配多行文本塊的正則表達式:

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

如果每個文件只有一個氨基酸序列,我根本不會使用正則表達式。 就像這樣:

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

尋找:

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

\1 = some_variing_text

\2 = 所有大寫字母的行

編輯(證明這有效):

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)]

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

我的偏好。

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 )

此時,您將 someVaryingText 作為字符串,將酸作為字符串列表。 您可以執行"".join( acids )來制作單個字符串。

我發現這比多行正則表達式不那么令人沮喪(而且更靈活)。

有時直接在字符串中指定標志,作為inline-flag會很舒服:

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

例如在單元測試中,使用assertRaisesRegex 這樣,您無需在調用斷言之前import re或編譯您的正則表達式。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM