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.

È stato utile?

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.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top