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


105

여러 줄에 걸친 텍스트와 일치시킬 때 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로 만들고 싶습니다.

궁금한 사람이 있다면 단백질을 구성하는 일련의 아미노산이어야합니다.


파일에 첫 번째 줄과 대문자 텍스트 외에 다른 것이 있습니까? 개행 문자에서 모든 텍스트를 분할하고 첫 번째 요소를 "some_Varying_TEXT"로 취하는 대신 정규식을 사용하는 이유를 잘 모르겠습니다.
UncleZeiv

2
예, 정규식은 이것에 대한 잘못된 도구입니다.

샘플 텍스트에 선행 >문자 가 없습니다 . 그럴까요?
MiniQuark

답변:


114

이 시도:

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

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

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

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

BTW, 여기서 DOTALL 수정자를 사용하고 싶지 않습니다. 점이 줄 바꿈을 제외한 모든 것과 일치한다는 사실에 의존 하고 있습니다.


이 정규식이 빈 두 번째 줄이있는 거의 모든 텍스트 파일과 일치하지 않도록하려면 정규식의 두 번째 점을 [AZ]로 대체 할 수 있습니다. ;-)
MiniQuark

내 인상은 대상 파일이 빈 줄과 비어 있지 않은 줄의 명확한 (그리고 반복되는) 패턴을 따르기 때문에 [AZ]를 지정할 필요는 없지만 아마 아프지 않을 것입니다.
Alan Moore

이 솔루션은 훌륭하게 작동했습니다. 제쳐두고, 나는 분명히 상황을 충분히 설명하지 않았기 때문에 사과합니다. 당신의 도움을 주셔서 감사합니다!
Jan

21

이것은 작동합니다.

>>> 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)+) 은 하나 이상의 textline 과 일치 하지만 각 줄을 그룹에 넣지 않음을 의미 합니다. 대신 넣어 모든 textlines을 한 그룹.
  • \n끝에 이중 줄 바꿈을 적용하려면 정규식에 final 을 추가 할 수 있습니다 .
  • 또한 어떤 유형의 줄 바꿈을 얻을지 ( \n또는 \r또는 \r\n) 확실하지 않은 경우 모든 항목을 \n로 대체하여 정규식을 수정하십시오 (?:\n|\r\n?).

1
match ()는 대상 텍스트의 맨 처음에 하나의 일치 만 반환하지만 OP는 파일 당 수백 개의 일치 항목이 있다고 말했습니다. 대신 finditer ()를 원할 것이라고 생각합니다.
Alan Moore

6

각 파일에 아미노산 시퀀스가 ​​하나만 있으면 정규식을 전혀 사용하지 않습니다. 다음과 같이 :

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

단 하나만 있다면 가장 쉬운 방법이고, 더 많은 로직이 추가되면 더 많이 사용할 수 있습니다. 이 특정 데이터 세트에는 약 885 개의 단백질이 있으며 정규식이이를 처리 할 수 ​​있어야한다고 생각했습니다.
Jan

4

찾기:

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

\ 1 = some_varying_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])

불행히도이 정규 표현식은 빈 줄로 구분 된 대문자 그룹과도 일치합니다. 그래도 큰 문제가 아닐 수도 있습니다.
MiniQuark

coonj가 FASTA 파일을 좋아하는 것 같습니다. ;)
Andrew Dalke

4

다음은 여러 줄 텍스트 블록과 일치하는 정규식입니다.

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

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 )단일 문자열을 만들 .

여러 줄 정규식보다 덜 답답하고 유연합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.