development

여러 줄 텍스트 블록과 일치하는 정규식

big-blog 2020. 9. 16. 08:10
반응형

여러 줄 텍스트 블록과 일치하는 정규식


여러 줄에 걸친 텍스트와 일치시킬 때 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)

가장 큰 문제는 ^$앵커가 줄 바꿈과 일치 할 것으로 예상 하지만 그렇지 않다는 것입니다. 여러 줄 모드에서 줄 바꿈 ^바로 $ 의 위치 와 줄 바꿈 바로 의 위치를 찾습니다.

줄 바꿈 (\ n), 캐리지 리턴 (\ r) 또는 캐리지 리턴 + 줄 바꿈 (\ r \ n)으로 구성 될 수도 있습니다. 대상 텍스트가 줄 바꿈 만 사용하는지 확실하지 않은 경우보다 포괄적 인 정규식 버전을 사용해야합니다.

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

BTW, 여기서 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((?:[A-Z]+\n)+)

  • 첫 번째 문자 ( ^)는 "행의 시작에서 시작"을 의미합니다. 개행 자체와는 일치하지 않는다는 점에 유의하십시오 ($의 경우 동일 : "개행 바로 앞"을 의미하지만 개행 자체와는 일치하지 않음).
  • 그런 다음 (.+?)\n\n"두 줄 바꿈에 도달 할 때까지 가능한 한 적은 수의 문자 (모든 문자 허용) 일치"를 의미합니다. 결과 (줄 바꿈없이)는 첫 번째 그룹에 배치됩니다.
  • [A-Z]+\n"줄 바꿈에 도달 할 때까지 가능한 한 많은 대문자와 일치합니다. 이것은 내가 textline 이라고 부르는 것을 정의합니다 .
  • ((?:textline)+) means match one or more textlines but do not put each line in a group. Instead, put all the textlines in one group.
  • You could add a final \n in the regular expression if you want to enforce a double newline at the end.
  • Also, if you are not sure about what type of newline you will get (\n or \r or \r\n) then just fix the regular expression by replacing every occurrence of \n by (?:\n|\r\n?).

If each file only has one sequence of aminoacids, I wouldn't use regular expressions at all. Just something like this:

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

find:

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

\1 = some_varying_text

\2 = lines of all CAPS

Edit (proof that this works):

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

The following is a regular expression matching a multiline block of text:

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

My preference.

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 )

At this point you have someVaryingText as a string, and the acids as a list of strings. You can do "".join( acids ) to make a single string.

I find this less frustrating (and more flexible) than multiline regexes.

참고URL : https://stackoverflow.com/questions/587345/regular-expression-matching-a-multiline-block-of-text

반응형