Seite 1 von 1
regulärer Ausdruck - suchen erst nach einem bestimmten Wort
Verfasst: Montag 21. März 2011, 14:20
von kleiner.epsilon
Hallo,
in einem langen Text möchte ich mit regulären Ausdrücken nach folgendem suchen:
Residual: 0.1234
Ich weiß aber, dass 'Residual' genau zweimal vorkommt, möchte aber nur das zweite, das irgendwann nach 'Parameter' steht und auch erst ungefähr nach der 1000. Position.
Meine erste Idee war die Position, nach der er erst suchen soll, festzulegen, aber das gefällt mir nicht.
Code: Alles auswählen
with open( ausgabetext ) as f2:
text = f2.read()
re11 = re.compile('Residual: [+|-]?\d[.]\d*')
re11.search(text,1000)
Meine zweite Idee funktionert aber auch nicht:
Gibt's da noch eine andere Möglichkeit?
Über ein paar Tipps wäre ich sehr dankbar.
Re: regulärer Ausdruck - suchen erst nach einem bestimmten W
Verfasst: Montag 21. März 2011, 14:28
von sma
Einmal suchen. Wenn gefunden, steht in match.end() die Position wo's aufhört. Von dort weitersuchen
Code: Alles auswählen
def find_second(p, s):
m = re.search(p, s)
if m:
e = m.end()
m = re.search(p, s[e:])
if m:
return m.start() + e
return -1
Stefan
Re: regulärer Ausdruck - suchen erst nach einem bestimmten W
Verfasst: Montag 21. März 2011, 14:37
von BlackJack
@sma: Man kann bei `re.search()` auch einen Startindex als Zahl angeben, dann braucht man die Teilzeichenkette nicht kopieren.
Edit: Zumindest bei der `search()`-Methode auf kompilierten Mustern gibt es diese Möglichkeit.
@kleiner.epsilon: Mit `finditer()` bekommt man einen Iterator über alle Treffer, da kann man einfach das erste Ergebnis wegwerfen und das zweite nehmen:
Code: Alles auswählen
residual_re = re.compile('Residual: ([+-]?\d[.]\d*)')
matches = residual_re.finditer(text)
matches.next() # Skip first.
residual = matches.next().group(1)
In Deiner `re` war übrigens ein '|' zuviel. Das hätte auch 'Residual: |42.23' als Treffer angesehen.
Re: regulärer Ausdruck - suchen erst nach einem bestimmten W
Verfasst: Montag 21. März 2011, 15:12
von sma
BlackJack hat geschrieben:@sma: Man kann bei `re.search()` auch einen Startindex als Zahl angeben, dann braucht man die Teilzeichenkette nicht kopieren.
Mir war so, ich hatte extra geschaut, aber die Doku sagte:
Code: Alles auswählen
>>> help(re.search)
Help on function search in module re:
search(pattern, string, flags=0)
Scan through string looking for a match to the pattern, returning
a match object, or None if no match was found.
Also glaubte ich ihr. Warum kann denn die Modul-Funktion weniger als die Methode? Grummel.
Stefan
Re: regulärer Ausdruck - suchen erst nach einem bestimmten W
Verfasst: Montag 21. März 2011, 23:08
von kleiner.epsilon
mmmh: also wenn ich folgende habe:
Code: Alles auswählen
residual_re = re.compile('Residual: [-]?\d[.]\d*')
matches = residual_re.finditer(text)
matches.next() # Skip first.
residual = matches.next().group(1)
kommt bei mir folgende Fehlermeldung:
Traceback (most recent call last):
File "rahmenprogramm-neu.py", line 111, in <module>
residual = matches.next().group(1)
StopIteration
Habe versucht etwas zu ändern und verschiedenes ausprobiert, bin nun etwas ratlos.
Re: regulärer Ausdruck - suchen erst nach einem bestimmten W
Verfasst: Montag 21. März 2011, 23:16
von deets
Das kommt dann, wenn dein Wort *garnicht* gefunden wird. Dann hat der Iterator nix zu iterieren, und dann kommt die Exception.
Vielleicht waere sowas hier fuer dich besser (ungetestet):
Code: Alles auswählen
for i, result in enumerate(residual_re.finditer(text)):
if i == 1: # skip the first iteration
break
else:
raise Exception("residual_re hat nix gefunden")
print result
Und **ACHTUNG**: du hast ja einen gewissen Ruf, Exceptions einfach wegzufangen - das solltest hier natuerlich NICHT machen... denn ich denke mal, es ist ein wirklicher Fehler, wenn kein Ergebnis da ist.
Re: regulärer Ausdruck - suchen erst nach einem bestimmten W
Verfasst: Montag 21. März 2011, 23:42
von BlackJack
@deets: Doch es wurde einmal gefunden -- sonst hätte das erste `next()` schon die `StopIteration` gebracht.
Re: regulärer Ausdruck - suchen erst nach einem bestimmten W
Verfasst: Montag 21. März 2011, 23:46
von deets
@BlackJack
Stimmt, ja - aber meine Loesung sollte trotzdem robust sein, weil der else-zweig sowohl bei keiner als auch bei nur einer Loesung durchlaufen wird.
Re: regulärer Ausdruck - suchen erst nach einem bestimmten W
Verfasst: Dienstag 22. März 2011, 00:15
von kleiner.epsilon
Die Ausdrücke kommen aber ganz sicher zweimal vor!
Folgendes hat jetzt funktioniert:
Code: Alles auswählen
re12 = re.finditer('Residual: [-]?\d*[.]\d*',text)
re12.next()
a12 = re12.next().group()
b12 = a12.split()
print b12[1]
Wie kann ich das etwas 'schöner' schreiben?
Re: regulärer Ausdruck - suchen erst nach einem bestimmten W
Verfasst: Dienstag 22. März 2011, 08:50
von BlackJack
@kleiner.epsilon: Kommen die *ganz* sicher zweimal vor? Du hast nämlich jetzt den regulären Ausdruck etwas verändert -- der ursprüngliche hat nur Zahlen gefunden die genau eine Ziffer vor dem Dezimalpunkt stehen haben. Nun ist da noch ein Sternchen hinzugekommen.
Diese Nummerierung von Namen ist fast immer ein Zeichen von einem Entwurfsproblem. Meistens ist das ein Hinweis darauf, dass man eine Liste verwenden sollte. Hier ist das die Folge von einem immer wieder kehrenden Muster wo Du immer neue Namen brauchst. Das ist ein Hinweis darauf, dass man eine Funktion schreiben sollte, in der dieses Muster dann nur einmal steht.
Andererseits sind die Namen so nichtssagend. Wenn Du Zwischenergebnisse an Namen wie `a12` bindest, die überhaupt nichts zum Verständnis des Quelltextes beitragen, dann solltest Du das Zwischenergebnis am besten überhaupt gar nicht erst an einen Namen binden. Und die Objekte, die Du brauchst, sollten aussagekräftigere Namen als `re12` haben. Bezogen auf Deinen Quelltext:
Code: Alles auswählen
residual_matches = re.finditer('Residual: [-]?\d*[.]\d*', text)
residual_matches.next()
residual = residual_matches.next().group().split()[1]
print residual
Da das Muster mit dem Suchen ab einem bestimmten Versatz in den `text` öfter vorkommt, sollte man das aber vielleicht in eine Funktion kapseln. Auf Grundlage von deets' Ansatz (ungetestet):
Code: Alles auswählen
def re_find_nth(pattern, text, index=1, flags=0):
"""Find the n-th match. Counting starts with zero.
Default index is 1 == the second match.
Returns `None` if there are not enough matches.
"""
for i, match in enumerate(re.finditer(pattern, text, flags)):
if i == index:
return match
return None
# …
residual = re_find_nth(r'Residual: (-?\d*\.\d*)', text).group(1)
Re: regulärer Ausdruck - suchen erst nach einem bestimmten W
Verfasst: Dienstag 22. März 2011, 11:35
von kleiner.epsilon
@BlackJack: Echt cool, danke!
Ja bei der Vergabe der Namen habe ich geschlampt, weil das Programm noch in der Testphase ist, aber ich sollte von Anfang die Namen ordentlich benennen.
Ich habe noch eine kleine Frage zu der Funktion:
Was bedeutet das 'r' vor dem String? Warum muss das dort sein? Warum die Klamer () ?
Code: Alles auswählen
residual = re_find_nth(r'Residual: (-?\d*\.\d*)', text).group().split()[1]
Übrigens hat sich durch deine Hilfe bei der Definition der Funktion gleich schon ein zweites Problem von mir gelöst.
In meinem Text suche ich nach residual, p1, p2, p3, p4 und p5, die kommen genau zweimal vor und ich möchte nur den zweiten.
Außerdem suche nach divergence, convergence, matrix und squares, die kommen nur einmal vor.
Das kann ich nun im Funktionsaufruf einstellen.
Danke.
Code: Alles auswählen
def re_find_nth(pattern, text, index=1, flags=0):
"""Find the n-th match. Counting starts with zero.
Default index is 1 == the second match.
Returns `None` if there are not enough matches.
"""
for i, match in enumerate(re.finditer(pattern, text, flags)):
if i == index:
return match
return None
residual = re_find_nth(r'Residual: (-?\d*\.\d*)', text).group().split()[1]
parameter1 = re_find_nth('p1: -?\d*\.?\d*', text).group().split()[1]
parameter2 = re_find_nth('p2: -?\d*\.?\d*', text).group().split()[1]
parameter3 = re_find_nth('p3: -?\d*\.?\d*', text).group().split()[1]
parameter4 = re_find_nth('p4: -?\d*\.?\d*', text).group().split()[1]
parameter5 = re_find_nth('p5: -?\d*\.?\d*', text).group().split()[1]
squares = re_find_nth('SQUARES \s*-?\d*\.\d*[E]?[+-]?\d?\d?\d?', text, index=0).group().split()[1]
convergence = re_find_nth('No corrector convergence', text, index=0)
divergence = re_find_nth('divergence', text, index=0)
matrix = re_find_nth('Matrix', text, index=0)
Übrigens hat sich durch deine Hilfe bei der Definition der Funktion gleich schon ein zweites Problem von mir gelöst.
In meinem Text suche ich nach residual, p1, p2, p3, p4 und p5, die kommen genau zweimal vor und ich möchte nur den zweiten.
Außerdem suche nach divergence, convergence, matrix und squares, die kommen nur einmal vor.
Das kann ich nun im Funktionsaufruf einstellen.
Danke.
Re: regulärer Ausdruck - suchen erst nach einem bestimmten W
Verfasst: Dienstag 22. März 2011, 12:16
von Leonidas
kleiner.epsilon hat geschrieben:Was bedeutet das 'r' vor dem String? Warum muss das dort sein? Warum die Klamer () ?
Das r steht für Raw-Strings und bedeutet dass Escape-Sequenzen wie ``\n`` nicht ausgewertet werden. Das heißt aber auch, dass man Backslashes, die doch relativ häufig in Regular Expressions vorkommen nicht escapen muss, und das macht sie etwas übersichtlicher. Die Klammer hingegen bedeutet, dass der ganze Match in einer Gruppe gefangen wird, worauf du etwa mit ``matchobjekt.groups(n)`` zugreifen kannst.
Re: regulärer Ausdruck - suchen erst nach einem bestimmten W
Verfasst: Dienstag 22. März 2011, 12:19
von BlackJack
@kleiner.epsilon: 'r' vor einem Zeichenkettenliteral bedeutet, dass die Backslashes in dem Literal für den Compiler keine besondere Bedeutung mehr haben. '\n' ist ein Zeichen -- nämlich das Zeilenende-Zeichen; r'\n' sind zwei Zeichen, nämlich ein Backslash und ein 'n'. Bei dem Beispiel macht es keinen Unterschied, aber da Backslashes in regulären Ausdrücken auch eine besondere Bedeutung haben, wird es manchmal verwirrend, wenn man Anfangen muss die Sachen doppelt zu escapen -- einmal für den Python-Compiler und dann für den Compiler die den regulären Ausdruck verarbeitet.
Die Klammer ist eine Gruppe im regulären Ausdruck. Schau Dir doch noch mal meinen Code weiter oben an. Da benutze ich klein ``.group().split()[1]`` sondern nur ``.group(1)`` um die erste Gruppe aus dem Treffer zu bekommen. Die Zählung beginnt da bei 1. Die Gruppe 0 ist immer der gesamte Treffer.
Die Einrückung der Funktionsdefinition lässt vermuten das das innerhalb der Schleife steht. Das bedeutet, dass diese Funktion bei jedem Schleifendurchlauf aufs Neue definiert wird. Das ist unnötige Arbeit. Nicht viel, aber IMHO unschön.
Re: regulärer Ausdruck - suchen erst nach einem bestimmten W
Verfasst: Dienstag 22. März 2011, 12:50
von kleiner.epsilon
Aha, hab verstanden. Danke.
Nun ist ein weiteres Problem entstanden: Was bedeutet diese Fehlermeldung?:
Traceback (most recent call last):
File "rahmenprogramm-neu.py", line 91, in <module>
residual = re_find_nth(r'Residual: (-?\d*\.\d*)', text).group(1)
AttributeError: 'NoneType' object has no attribute 'group'
Meine Vermutung ist, dass es etwas mit der Funktiondefinition zu tun hat.
Ich möchte nämlich eigentlich, dass wenn er gar keinen Treffer gefunden hat, dass er dann nichts macht,
und das Programm dann einfach im else-Zweig, der noch kommt, weiter läuft. Ist das so hier?
Code: Alles auswählen
def re_find_nth(pattern, text, index=1, flags=0):
"""Find the n-th match. Counting starts with zero.
Default index is 1 == the second match.
Returns `None` if there are not enough matches.
"""
for i, match in enumerate(re.finditer(pattern, text, flags)):
if i == index:
return match
return None
Re: regulärer Ausdruck - suchen erst nach einem bestimmten W
Verfasst: Dienstag 22. März 2011, 13:11
von BlackJack
@kleiner.epsilon: Die Funktion gibt `None` zurück wenn kein Treffer im Text ist, beziehungsweise wenn es nicht genug Treffer gibt um den n-ten zu liefern. Das steht doch im Docstring der Funktion.
Und wenn sie `None` zurück gibt, dann kann man darauf nicht `group()` Aufrufen. Das sagt Dir die Fehlermeldung. Also müsstest Du die Rückgabe auf `None` prüfen. Genau wie man das bei den normalen `re`-Methoden auch machen müsste.
Re: regulärer Ausdruck - suchen erst nach einem bestimmten W
Verfasst: Dienstag 22. März 2011, 13:59
von kleiner.epsilon
Tut mir leid, ich verstehe das nicht.
Also das man auf 'None' nicht 'group()' aufrufen kann, ist klar.
Aber deine letzten beide Sätze habe ich nicht verstanden, sorry.
Re: regulärer Ausdruck - suchen erst nach einem bestimmten W
Verfasst: Dienstag 22. März 2011, 14:06
von Hyperion
kleiner.epsilon hat geschrieben:Tut mir leid, ich verstehe das nicht.
Also das man auf 'None' nicht 'group()' aufrufen kann, ist klar.
Wenn das klar ist, was ist dann an den folgenden Sätzen nicht klar?
Code: Alles auswählen
# statt
residual = re_find_nth(r'Residual: (-?\d*\.\d*)', text).group().split()[1]
# eben so
result = re_find_nth(r'Residual: (-?\d*\.\d*)', text)
if result is not None:
residual = result.group().split()[1]