2009-02-25 11 views
60

Tengo problemas para obtener una expresión regular de Python que funcione cuando se combina con texto que abarca varias líneas. El texto es ejemplo ('\ n' es un salto de línea)Expresión regular que coincide con un bloque de texto de líneas múltiples

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 debajo de ella en una captura (puedo quitar los caracteres de nueva línea más tarde). 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, una por una, que no es lo que realmente quiero. Puedo ver la primera parte, no hay problema, pero parece que no puedo captar las 4 o 5 líneas de texto en mayúsculas. Me gustaría que match.group (1) sea un _ Variable _ El texto y el grupo (2) sean line1 + line2 + line3 + etc hasta que se encuentre la línea vacía.

Si alguien tiene curiosidad, se supone que es una secuencia de aminoácidos que forman una proteína.

+0

¿Hay algo más en el archivo además de la primera línea y el texto en mayúsculas? No estoy seguro de por qué usaría una expresión regular en lugar de dividir todo el texto en caracteres de nueva línea y tomar el primer elemento como "some_Varying_TEXT". – UncleZeiv

+2

sí, la expresión regular es la herramienta incorrecta para esto. – hop

+0

Su texto de muestra no tiene un carácter '>' inicial. ¿Deberia? – MiniQuark

Respuesta

81

Prueba esto:

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

Creo que su mayor problema es que usted está esperando los ^ y $ anclas para que coincida con los avances de línea, pero no lo hacen. En el modo multilínea, ^ coincide con la posición inmediatamente siguiente una nueva línea y $ coincide con la posición inmediatamente anterior a una nueva línea.

Tenga en cuenta también que una nueva línea puede consistir en un salto de línea (\ n), un retorno de carro (\ r) o un retorno de carro + salto 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, que no quieren utilizar el modificador dotall aquí; confía en el hecho de que el punto coincide con todo excepto líneas nuevas.

+0

Es posible que desee reemplazar el segundo punto en la expresión regular por [A-Z] si no desea que esta expresión regular coincida con casi cualquier archivo de texto con una segunda línea vacía. ;-) – MiniQuark

+0

Mi impresión es que los archivos de destino se ajustarán a un patrón definido (y repetitivo) de líneas vacías frente a líneas no vacías, por lo que no debería ser necesario especificar [AZ], pero probablemente no duela. , ya sea. –

+0

Esta solución funcionó maravillosamente. Como comentario aparte, me disculpo, ya que obviamente no aclare suficientemente la situación (y también por la tardanza de esta respuesta). ¡Gracias por tu ayuda! – Jan

1

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

me parece incorrecto ¿Has probado esto? – Triptych

+0

Sí, agregué un código para ti. –

+0

Desafortunadamente, esta expresión regular también coincidirá con grupos de letras mayúsculas separadas por líneas vacías. Aunque tal vez no sea un gran problema. – MiniQuark

14

Este funcionará:

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

Algunos explicación sobre esta expresión regular podría 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 la nueva línea (lo mismo para $: significa "justo antes de una nueva línea", pero no coincide con la nueva línea).
  • Luego, (.+?)\n\n significa "unir la menor cantidad de caracteres posible (todos los caracteres están permitidos) hasta que llegue a dos nuevas líneas". El resultado (sin las nuevas líneas) se coloca en el primer grupo.
  • [A-Z]+\n significa "coincidir tantas letras mayúsculas como sea posible hasta llegar a una nueva línea. Esto define lo que llamaré una TextLine.
  • ((?:TextLine)+) significa partido de uno o más líneas de texto pero no lo hacen poner cada línea en un grupo. en su lugar, puso toda la líneas de texto en un grupo.
  • se podría añadir una final \n 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 se soluciona con la expresión regular mediante la sustitución de todas las apariciones de \n por (?:\n|\r\n?).
+0

match() solo devuelve una coincidencia, al principio del texto de destino, pero el OP dijo que habría cientos de coincidencias por archivo. Creo que querrías finditer() en su lugar. –

+1

@ Alan: solo arreglado, gracias. – MiniQuark

1

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 usted ha someVaryingText como una cadena, y los ácidos como una lista de cadenas. Puede hacer "".join(acids) para hacer una sola cadena.

Encuentro esto menos frustrante (y más flexible) que las expresiones regulares multilínea.

4

Si cada archivo solo tiene una secuencia de aminoácidos, no utilizaría ninguna expresión regular. Algo así:

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 
+0

Definitivamente la forma más fácil si solo hubiera una, y también es viable con más, si se agrega más lógica. Sin embargo, hay alrededor de 885 proteínas en este conjunto de datos específico, y sentí que una expresión regular debería poder manejar esto. – Jan

Cuestiones relacionadas