Regulärer Ausdruck, der einem mehrzeiligen Textblock entspricht
Lesezeit: 6 Minuten
Jan
Ich habe ein paar Probleme damit, einen Python-Regex zum Laufen zu bringen, wenn ich mit Text vergleiche, der sich über mehrere Zeilen erstreckt. Der Beispieltext ist (‘n’ ist ein Zeilenumbruch)
some Varying TEXTn
n
DSJFKDAFJKDAFJDSAKFJADSFLKDLAFKDSAFn
[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).
Ich möchte zwei Dinge erfassen: den Teil „some_Varying_TEXT“ und alle Textzeilen in Großbuchstaben, die zwei Zeilen darunter in einer Erfassung kommen (ich kann die Zeilenumbruchzeichen später entfernen). Ich habe es mit ein paar Ansätzen versucht:
re.compile(r"^>(w+)$$([.$]+)^$", re.MULTILINE) # try to capture both parts
re.compile(r"(^[^>][ws]+)$", re.MULTILINE|re.DOTALL) # just textlines
und viele Variationen davon ohne Glück. Der letzte scheint die Textzeilen eine nach der anderen abzugleichen, was ich nicht wirklich will. Ich kann den ersten Teil verstehen, kein Problem, aber ich kann die 4-5 Zeilen Text in Großbuchstaben nicht verstehen. Ich möchte, dass match.group(1) some_Varying_Text und group(2) line1+line2+line3+etc ist, bis die leere Zeile gefunden wird.
Falls es jemanden interessiert, es soll eine Abfolge von Aminosäuren sein, aus denen ein Protein besteht.
Gibt es außer der ersten Zeile und dem Großbuchstaben noch etwas anderes in der Datei? Ich bin mir nicht sicher, warum Sie eine Regex verwenden würden, anstatt den gesamten Text an Zeilenumbruchzeichen aufzuteilen und das erste Element als “some_Varying_TEXT” zu nehmen.
– Onkel Zeiv
25. Februar 09 um 19:20 Uhr
ja, Regex sind dafür das falsche Werkzeug.
– Benutzer3850
25. Februar 09 um 20:25 Uhr
Ihr Beispieltext hat keinen Zeilenabstand > Charakter. Sollte es?
– MiniQuark
25. Februar 09 um 20:39 Uhr
Alan Moore
Versuche dies:
re.compile(r"^(.+)n((?:n.+)+)", re.MULTILINE)
Ich denke, dein größtes Problem ist, dass du das erwartest ^ und $ Anker, um Zeilenumbrüchen zu entsprechen, aber sie tun es nicht. Im mehrzeiligen Modus ^ passt sofort auf die Position folgende ein Zeilenumbruch und $ passt sofort auf die Position vorangehend ein Zeilenumbruch.
Beachten Sie auch, dass ein Zeilenumbruch aus einem Zeilenvorschub bestehen kann (n), ein Wagenrücklauf (r) oder ein Wagenrücklauf+Zeilenvorschub (rn). Wenn Sie nicht sicher sind, ob Ihr Zieltext nur Zeilenvorschübe verwendet, sollten Sie diese umfassendere Version der Regex verwenden:
Übrigens, Sie möchten hier nicht den DOTALL-Modifikator verwenden; Sie verlassen sich darauf, dass der Punkt zu allem passt außer Zeilenumbrüche.
Möglicherweise möchten Sie den zweiten Punkt in der Regex durch ersetzen [A-Z] wenn Sie nicht möchten, dass dieser reguläre Ausdruck mit jeder Textdatei mit einer leeren zweiten Zeile übereinstimmt. 😉
– MiniQuark
25. Februar 09 um 20:36 Uhr
Mein Eindruck ist, dass die Zieldateien einem bestimmten (und sich wiederholenden) Muster aus leeren und nicht leeren Zeilen entsprechen, sodass eine Angabe nicht erforderlich sein sollte [A-Z]aber es wird wahrscheinlich auch nicht schaden.
– Alan Moore
25. Februar 09 um 21:13 Uhr
Diese Lösung hat wunderbar funktioniert. Nebenbei entschuldige ich mich dafür, dass ich den Sachverhalt offensichtlich nicht ausreichend aufgeklärt habe (und auch für die Verspätung dieser Antwort). Danke für Ihre Hilfe!
– Jan
3. März 09 um 22:18 Uhr
MiniQuark
Das wird funktionieren:
>>> import re
>>> rx_sequence=re.compile(r"^(.+?)nn((?:[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
Einige Erläuterungen zu diesem regulären Ausdruck könnten hilfreich sein: ^(.+?)nn((?:[A-Z]+n)+)
Das erste Zeichen (^) bedeutet „beginnend am Anfang einer Zeile“. Beachten Sie, dass es nicht mit dem Zeilenumbruch selbst übereinstimmt (dasselbe gilt für $: es bedeutet “kurz vor einem Zeilenumbruch”, aber es stimmt nicht mit dem Zeilenumbruch selbst überein).
Dann (.+?)nn bedeutet “passen Sie so wenig Zeichen wie möglich an (alle Zeichen sind erlaubt), bis Sie zwei Zeilenumbrüche erreichen”. Das Ergebnis (ohne die Zeilenumbrüche) wird in die erste Gruppe gestellt.
[A-Z]+n bedeutet “passen Sie so viele Großbuchstaben wie möglich an, bis Sie einen Zeilenumbruch erreichen. Dies definiert, was ich a nennen werde Textzeile.
((?:Textzeile)+) bedeutet Übereinstimmung mit einem oder mehreren Textzeilen aber fügen Sie nicht jede Zeile in eine Gruppe ein. Setzen Sie stattdessen alle das Textzeilen in einer Gruppe.
Sie könnten ein Finale hinzufügen n im regulären Ausdruck, wenn Sie am Ende einen doppelten Zeilenumbruch erzwingen möchten.
Wenn Sie sich nicht sicher sind, welche Art von Zeilenumbruch Sie erhalten (n oder r oder rn) korrigieren Sie dann einfach den regulären Ausdruck, indem Sie jedes Vorkommen von ersetzen n durch (?:n|rn?).
match() gibt nur eine Übereinstimmung ganz am Anfang des Zieltextes zurück, aber das OP sagte, dass es Hunderte von Übereinstimmungen pro Datei geben würde. Ich denke, Sie würden stattdessen finditer() wollen.
– Alan Moore
25. Februar 09 um 21:24 Uhr
Punnerud
Das Folgende ist ein regulärer Ausdruck, der mit einem mehrzeiligen Textblock übereinstimmt:
import re
result = re.findall('(startText)(.+)((?:n.+)+)(endText)',input)
Dies ist meiner Meinung nach die beste und direkteste Antwort.
– pauljohn32
25. März 21 um 15:43 Uhr
Wenn jede Datei nur eine Sequenz von Aminosäuren enthält, würde ich überhaupt keine regulären Ausdrücke verwenden. Nur so etwas:
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
Jason Coon
finden:
^>([^nr]+)[nr]([A-Znr]+)
1 = etwas_variierender_Text
2 = Zeilen nur GROSSBUCHSTABEN
Bearbeiten (Beweis, dass dies funktioniert):
text = """> some_Varying_TEXT
DSJFKDAFJKDAFJDSAKFJADSFLKDLAFKDSAF
GATACAACATAGGATACA
GGGGGAAAAAAAATTTTTTTTT
CCCCAAAA
> some_Varying_TEXT2
DJASDFHKJFHKSDHF
HHASGDFTERYTERE
GAGAGAGAGAG
PPPPPAAAAAAAAAAAAAAAP
"""
import re
regex = re.compile(r'^>([^nr]+)[nr]([A-Znr]+)', re.MULTILINE)
matches = [m.groups() for m in regex.finditer(text)]
for m in matches:
print 'Name: %snSequence:%s' % (m[0], m[1])
Leider passt dieser reguläre Ausdruck auch auf Gruppen von Großbuchstaben, die durch Leerzeilen getrennt sind. Es könnte jedoch keine große Sache sein.
– MiniQuark
25. Februar 09 um 20:27 Uhr
Sieht so aus, als ob coonj FASTA-Dateien mag. 😉
– Andreas Dalke
26. Februar 09 um 13:21 Uhr
S. Lott
Meine Vorliebe.
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 )
An diesem Punkt haben Sie someVaryingText als String und die Säuren als Liste von Strings. Du kannst tun "".join( acids ) um eine einzelne Saite zu machen.
Ich finde das weniger frustrierend (und flexibler) als mehrzeilige reguläre Ausdrücke.
Leider passt dieser reguläre Ausdruck auch auf Gruppen von Großbuchstaben, die durch Leerzeilen getrennt sind. Es könnte jedoch keine große Sache sein.
– MiniQuark
25. Februar 09 um 20:27 Uhr
Sieht so aus, als ob coonj FASTA-Dateien mag. 😉
– Andreas Dalke
26. Februar 09 um 13:21 Uhr
.
7577700cookie-checkRegulärer Ausdruck, der einem mehrzeiligen Textblock entsprichtyes
Gibt es außer der ersten Zeile und dem Großbuchstaben noch etwas anderes in der Datei? Ich bin mir nicht sicher, warum Sie eine Regex verwenden würden, anstatt den gesamten Text an Zeilenumbruchzeichen aufzuteilen und das erste Element als “some_Varying_TEXT” zu nehmen.
– Onkel Zeiv
25. Februar 09 um 19:20 Uhr
ja, Regex sind dafür das falsche Werkzeug.
– Benutzer3850
25. Februar 09 um 20:25 Uhr
Ihr Beispieltext hat keinen Zeilenabstand
>
Charakter. Sollte es?– MiniQuark
25. Februar 09 um 20:39 Uhr