espressione regolare corrispondenza di un blocco di testo multilinea di
Domanda
Sto avendo un po 'di problemi a trovare una regex di Python per lavorare quando la corrispondenza con il testo che si estende su più righe. Il testo esempio è ( '\ n' è una nuova riga)
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).
Mi piacerebbe catturare due cose: la parte 'some_Varying_TEXT', e tutte le righe di testo maiuscolo che arriva due righe di sotto di essa in un'acquisizione (posso togliere i caratteri di nuova riga più avanti). Ho provato con alcuni approcci:
re.compile(r"^>(\w+)$$([.$]+)^$", re.MULTILINE) # try to capture both parts
re.compile(r"(^[^>][\w\s]+)$", re.MULTILINE|re.DOTALL) # just textlines
e un sacco di variazioni del presente documento senza fortuna. L'ultimo sembra corrispondere le righe di testo a uno a uno, che non è quello che voglio veramente. Riesco ad afferrare la prima parte, non è un problema, ma io non riesco a cogliere le 4-5 righe di testo maiuscolo. Mi piacerebbe match.group (1) da some_Varying_Text e di gruppo (2) di essere riga1 + linea2 + line3 + ecc fino a quando si incontra la riga vuota.
Se qualcuno è curioso, la sua doveva essere una sequenza di amminoacidi che compongono una proteina.
Soluzione
Prova questo:
re.compile(r"^(.+)\n((?:\n.+)+)", re.MULTILINE)
Credo che il problema più grande è che vi aspettate le ancore ^
e $
per abbinare linefeeds, ma non lo fanno. In modalità multilinea, ^
corrisponde alla posizione immediatamente dopo una nuova riga e $
corrisponde alla posizione immediatamente precedente una nuova riga.
Essere consapevoli, anche, che un ritorno a capo può essere costituito da un avanzamento riga (\ n), un ritorno a capo (\ r), o un ritorno a capo + avanzamento riga (\ r \ n). Se non si è certi che il vostro testo di arrivo utilizza solo linefeeds, è necessario utilizzare questa versione più inclusiva della regex:
re.compile(r"^(.+)(?:\n|\r\n?)((?:(?:\n|\r\n?).+)+)", re.MULTILINE)
A proposito, non si desidera utilizzare il modificatore DOTALL qui; stai contando sul fatto che il punto corrisponde a tutto tranne a capo.
Altri suggerimenti
Questo funziona:
>>> 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
Alcuni spiegazione su questa espressione regolare potrebbe essere utile: ^(.+?)\n\n((?:[A-Z]+\n)+)
- Il primo carattere (
^
) significa "a partire dall'inizio di una riga". Essere consapevoli del fatto che non corrisponde alla nuova riga stessa. (Lo stesso per $: significa "poco prima di un ritorno a capo", ma non corrisponde alla nuova linea stessa) - Poi
(.+?)\n\n
significa "corrisponde il minor numero di caratteri possibile (tutti i caratteri sono consentiti) fino a raggiungere due a capo". Il risultato (senza ritorni a capo) viene messo nel primo gruppo. -
[A-Z]+\n
significa "abbinare quante lettere maiuscole come possibile fino a raggiungere una nuova riga. Questo definisce quello che chiamerò un TextLine . -
((?:
TextLine)+)
significa partita uno o più oggetti TextLine , ma non mettere ogni riga in un gruppo. Invece, mettere tutti oggetti TextLine in un gruppo. - Si potrebbe aggiungere un
\n
finale nell'espressione regolare se si vuole applicare una doppia nuova riga alla fine. - Inoltre, se non siete sicuri su quale tipo di nuova linea si otterrà (
\n
o\r
o\r\n
) poi basta fissare l'espressione regolare sostituendo ogni occorrenza di\n
da(?:\n|\r\n?)
.
Se ogni file ha una sola sequenza di aminoacidi, non vorrei usare le espressioni regolari a tutti. Solo qualcosa di simile:
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 = linee di tutte le CAPS
Modifica (la prova che questo funziona):
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])
La seguente è un'espressione regolare che corrisponde un blocco di testo multilinea di:
import re
result = re.findall('(startText)(.+)((?:\n.+)+)(endText)',input)
La mia preferenza.
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 )
A questo punto si hanno someVaryingText come una stringa, e gli acidi come una lista di stringhe.
Si può fare "".join( acids )
per fare una singola stringa.
Lo trovo meno frustrante (e più flessibile) di espressioni regolari multilinea.