web-dev-qa-db-fra.com

Expression régulière correspondant à un bloc de texte multiligne

J'ai un peu de difficulté à obtenir une expression rationnelle Python lors de la comparaison avec un texte couvrant plusieurs lignes. Le texte d'exemple est ('\ n' est une nouvelle ligne)

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

Je voudrais capturer deux choses: la partie 'some_Varying_TEXT' et toutes les lignes de texte majuscule qui viennent deux lignes en dessous dans une capture (je peux supprimer les caractères de nouvelle ligne plus tard). J'ai essayé avec quelques approches:

re.compile(r"^>(\w+)$$([.$]+)^$", re.MULTILINE) # try to capture both parts
re.compile(r"(^[^>][\w\s]+)$", re.MULTILINE|re.DOTALL) # just textlines

et beaucoup de variations de celui-ci sans aucune chance. Le dernier semble correspondre aux lignes de texte une par une, ce qui n’est pas ce que je veux vraiment. Je peux attraper la première partie, pas de problème, mais je n'arrive pas à attraper les 4-5 lignes de texte en majuscule. Je voudrais que match.group (1) soit some_Varying_Text et que group (2) soit line1 + line2 + line3 + etc jusqu'à ce que la ligne vide soit rencontrée.

Si quelqu'un est curieux, il est supposé être une séquence d'acides aminés constituant une protéine.

84
Jan

Essaye ça:

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

Je pense que votre plus gros problème est que vous attendez le ^ et $ _ ancres pour correspondre aux sauts de ligne, mais ils ne le font pas. En mode multiligne, ^ correspond à la position immédiatement suivant une nouvelle ligne et $ correspond à la position immédiatement previous une nouvelle ligne.

Sachez également qu’une nouvelle ligne peut consister en un saut de ligne (\ n), un retour chariot (\ r) ou un retour chariot + retour à la ligne (\ r\n). Si vous n'êtes pas certain que votre texte cible utilise uniquement des sauts de ligne, vous devez utiliser cette version plus inclusive de l'expression régulière:

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

BTW, vous ne voulez pas utiliser le modificateur DOTALL ici; vous vous fiez au fait que le point correspond à tout sauf nouvelles lignes.

105
Alan Moore

Cela fonctionnera:

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

Quelques explications sur cette expression régulière pourraient être utiles: ^(.+?)\n\n((?:[A-Z]+\n)+)

  • Le premier caractère (^) Signifie "à partir du début d'une ligne". Sachez que cela ne correspond pas à la nouvelle ligne (idem pour $: cela signifie "juste avant une nouvelle ligne", mais cela ne correspond pas à la nouvelle ligne elle-même).
  • Alors (.+?)\n\n Signifie "faites correspondre le moins de caractères possible (tous les caractères sont autorisés) jusqu'à ce que vous atteigniez deux nouvelles lignes". Le résultat (sans les nouvelles lignes) est placé dans le premier groupe.
  • [A-Z]+\n Signifie "faire correspondre autant de lettres majuscules que possible jusqu'à atteindre une nouvelle ligne. Ceci définit ce que j'appellerai une ligne de texte .
  • ((?: textline )+) Signifie qu’il faut faire correspondre une ou plusieurs textlines mais ne mettez pas chaque ligne dans un groupe. Au lieu de cela, mettez tous les textlines dans un groupe.
  • Vous pouvez ajouter un dernier \n Dans l'expression régulière si vous souhaitez imposer une double nouvelle ligne à la fin.
  • De plus, si vous n'êtes pas sûr du type de nouvelle ligne que vous obtiendrez (\n Ou \r Ou \r\n), Corrigez simplement l'expression régulière en remplaçant chaque occurrence de \n Par (?:\n|\r\n?).
20
MiniQuark

Si chaque fichier ne contient qu'une séquence d'acides aminés, je n'utiliserais pas du tout d'expressions régulières. Juste quelque chose comme ça:

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

trouver:

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

\ 1 = some_varying_text

\ 2 = lignes de tous les CAPS

Edit (preuve que cela fonctionne):

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])
3
Jason Coon

Voici une expression régulière correspondant à un bloc de texte multiligne:

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

Ma préférence.

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 )

À ce stade, vous avez someVaryingText en tant que chaîne et les acides en tant que liste de chaînes. Vous pouvez faire "".join( acids ) pour créer une seule chaîne.

Je trouve cela moins frustrant (et plus flexible) que les regex multilignes.

1
S.Lott