de expresiones regulares de un bloque de varias líneas de texto
Pregunta
Estoy teniendo un poco de problemas para conseguir una expresión regular de Python para trabajar cuando se compara contra el texto que abarca varias líneas. El texto ejemplo es ( '\ n' es un salto de línea)
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).
Me gustaría capturar dos cosas: la parte 'some_Varying_TEXT', y todas las líneas de texto en mayúsculas que se produce dos líneas por debajo de ella en una captura (i puede despojar a cabo los caracteres de nueva línea más adelante). He intentado con algunos enfoques:
re.compile(r"^>(\w+)$$([.$]+)^$", re.MULTILINE) # try to capture both parts
re.compile(r"(^[^>][\w\s]+)$", re.MULTILINE|re.DOTALL) # just textlines
y una gran cantidad de variaciones del presente sin suerte. El último parece coincidir con las líneas de texto uno a uno, que no es lo que realmente quiero. Soy capaz de captar la primera parte, no hay problema, pero me parece que no puede atrapar a los 4-5 líneas de texto en mayúsculas. Me gustaría match.group (1) que se some_Varying_Text y el grupo (2) para ser línea 1 + line2 + línea3 + etc hasta que se encuentra la línea de vacío.
Si alguien tiene curiosidad, se supone que es una secuencia de aminoácidos que forman una proteína.
Solución
Prueba esto:
re.compile(r"^(.+)\n((?:\n.+)+)", re.MULTILINE)
Creo que su mayor problema es que usted está esperando los anclajes ^
y $
para que coincida con los avances de línea, pero no lo hacen. En el modo de multilínea, ^
coincide con la posición inmediatamente siguiente un salto de línea y $
coincida con la posición inmediatamente anterior una nueva línea.
Tenga en cuenta, también, que un salto de línea puede consistir en un avance de línea (\ n), un retorno de carro (\ r), o un retorno de carro + avance de línea (\ r \ n). Si no está seguro de que el texto de destino utiliza sólo los avances de línea, se debe utilizar esta versión más inclusiva de la expresión regular:
re.compile(r"^(.+)(?:\n|\r\n?)((?:(?:\n|\r\n?).+)+)", re.MULTILINE)
Por cierto, usted no desea utilizar el modificador dotall aquí; usted está confiando en el hecho de que el punto coincide con todo lo excepto saltos de línea.
Otros consejos
Esto funciona:
>>> 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
Algunas explicaciones acerca de esta expresión regular puede ser útil: ^(.+?)\n\n((?:[A-Z]+\n)+)
- El primer carácter (
^
) significa "empezando por el principio de una línea". Tenga en cuenta que no coincide con el salto de línea en sí. (Lo mismo para $: significa "justo antes de una nueva línea", pero no coincide con el salto de línea en sí) - A continuación,
(.+?)\n\n
significa "coincide con el menor número de caracteres como sea posible (todos los caracteres están permitidos) hasta llegar a dos saltos de línea". El resultado (sin los saltos de línea) se pone en el primer grupo. -
[A-Z]+\n
significa "coincide con el mayor número de letras mayúsculas como sea posible hasta que llegue una nueva línea. Esto define lo que llamaré un línea de texto . -
((?:
línea de texto significa)+)
partido de uno o más líneas de texto , pero no ponga cada línea en un grupo. En su lugar, poner todos líneas de texto en un grupo. - Se podría añadir un
\n
final en la expresión regular si quiere cumplir un doble salto de línea al final. - Además, si usted no está seguro sobre qué tipo de salto de línea obtendrá (
\n
o\r
o\r\n
) a continuación, sólo fijar la expresión regular mediante la sustitución de todas las apariciones de\n
por(?:\n|\r\n?)
.
Si cada archivo sólo tiene una secuencia de aminoácidos de, yo no utilizar expresiones regulares en absoluto. Sólo algo como esto:
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
hallazgo:
^>([^\n\r]+)[\n\r]([A-Z\n\r]+)
\ 1 = some_varying_text
\ 2 = líneas de todos los CAPS
Editar (prueba de que esto funciona):
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 siguiente es una expresión regular que coincida con un bloque de varias líneas de texto:
import re
result = re.findall('(startText)(.+)((?:\n.+)+)(endText)',input)
Mi preferencia.
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 )
En este punto se han someVaryingText como una cadena, y los ácidos como una lista de cadenas.
Usted puede hacer "".join( acids )
para hacer una sola cadena.
Me parece menos frustrante (y más flexible) de expresiones regulares de varias líneas.