Probs mit Re

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Gast

Hi @ all, ich habe das Gefühl im Modul re ist ein Bug, weil ich wirklich keinen Fehler finden kann:

Ein Regulärer Ausdruck soll wie üblich einen ganz bestimmten Teil in einem String herausfiltern, aber es tut es nicht wie er es sollte. Wo liegt mein Fehler?:

Code: Alles auswählen

>>> import re
>>> text="""<td><a href="/cgi-bin/info.pl?sid=EEDFMVQFIEXGQDTR&alli=R42">Die Heilige Suche</a> [R42]</td></tr><tr><td>Metaallianz</td><td><a href="/cgi-bin/info.pl?sid=EEDFMVQFIEXGQDTR&meta=FAA">Foederation Auserwaehlter Allianzen</a> [FAA]</td></tr><tr><th colspan=2>Planeten (insgesamt 31)</th></tr><tr><td>Name (Koordinaten)</td><td>Punkte</td></tr><tr><td><a href="/cgi-bin/info.pl?sid=EEDFMVQFIEXGQDTR&coord=555763A">Meso</a> (555:763:A)</td><td>253</td>"""
>>> p=re.compile('<td><a href="/cgi-bin/info\.pl.*?">(.+?)</a> \(([\dA-Z:]+?)\)</td><td>([\d\.]+?)</td>',re.I)
>>> l=p.findall(text)
>>>
>>>
>>> l
[('Die Heilige Suche</a> [R42]</td></tr><tr><td>Metaallianz</td><td><a href="/cgi-bin/info.pl?sid=EEDFMVQFIEXGQDTR&meta=FAA">Foederation Auserwaehlter Allianzen</a> [FAA]</td></tr><tr><th colspan=2>Planeten (insgesamt 31)</th></tr><tr><td>Name (Koordinaten)</td><td>Punkte</td></tr><tr><td><a href="/cgi-bin/info.pl?sid=EEDFMVQFIEXGQDTR&coord=555763A">Meso', '555:763:A', '253')]
Stattdessen sollte er aber das hier liefern und dabei nur einen ganz anderen Teil (suchtext) verwerten:

Code: Alles auswählen

#suchtext="""<td><a href="/cgi-bin/info.pl?sid=EEDFMVQFIEXGQDTR&coord=555763A">Meso</a> (555:763:A)</td><td>253</td>"""
[('Meso', '555:763:A', '253')]
liegt der Fehler an mir oder an Python?
Voges
User
Beiträge: 564
Registriert: Dienstag 6. August 2002, 14:52
Wohnort: Region Hannover

Hallo!

Code: Alles auswählen

p=re.compile('<td><a href="/cgi-bin/info\.pl.*?">(.+?)</a> \( .....
                                              ^
                                  Das Fragezeichen muss weg
hth
Jan
Gast

Komisch... erklärt das nich gearde den "*" Operator als non-gredy ? :?:
Voges
User
Beiträge: 564
Registriert: Dienstag 6. August 2002, 14:52
Wohnort: Region Hannover

Namenloser hat geschrieben:Komisch... erklärt das nich gearde den "*" Operator als non-gredy ? :?:
Nein, ohne ? ist * greedy, liest also nicht bis zum ersten Auftreten von ">, sondern bis zum letztmöglichen.
Jan
Gast

Voges hat geschrieben:
Namenloser hat geschrieben:Komisch... erklärt das nich gearde den "*" Operator als non-gredy ? :?:
Nein, ohne ? ist * greedy, liest also nicht bis zum ersten Auftreten von ">, sondern bis zum letztmöglichen.
Jan
meinte ich ja ;) Ich wollte eigentlich das hier ausdrücken: erklärt das Fragezeichen nicht gerade...
Wenn ich das wegnehme, sucht er doch noch viel mehr und müsste eigentlich sich so verhalten, wie im ersten Beispiel von mir (wo mit "*?" non-gredey gesucht wird). Deswegen wundere ich mich ja auch, warum er über das "> hinwegsieht... :shock:
Voges
User
Beiträge: 564
Registriert: Dienstag 6. August 2002, 14:52
Wohnort: Region Hannover

Was Du nicht verstanden hast, habe ich nicht verstanden ;-)
Welcher re sieht über welches "> hinweg?

.*"> (greedy) gibt sich nicht mit dem ersten "> (vor "Die Heilige") zufrieden, sondern sucht das letztmögliche "> (vor "Meso"). Damit ist alles bis direkt vor "Meso" 'weggefressen' worden und der Rest des re kommt für den Rest des Textes zur Anwendung (etwas vereinfacht dargestellt).

.*?"> (non-greedy) liest nur bis zum ersten "> (vor "Die Heilige"). Der Rest des Textes ist Sache des Restes des re (wieder etwas ungenau dargestellt).

Jan
Gast

Das ist mir klar, so stehts ja auch in der Doku. Nur deswegen verstehe ich ja nicht, warum das hier unten von dir (mit nur "*" statt "*?") funktioniert. eigentlich müsste es ja umgekehrt funktionieren, dafür sollte der Rest von dem Regulären Ausdruck sorgen.
Voges hat geschrieben:Hallo!

Code: Alles auswählen

p=re.compile('<td><a href="/cgi-bin/info\.pl.*?">(.+?)</a> \( .....
                                              ^
                                  Das Fragezeichen muss weg
hth
Jan
Voges
User
Beiträge: 564
Registriert: Dienstag 6. August 2002, 14:52
Wohnort: Region Hannover

Hallo!
Jetzt verstehe ich gar nichts mehr ;-) .
Du willst doch, dass in Deinem 1. Beipiel nur "Meso" in der 1. Gruppenklammer landet, richtig?

Ich versuch's nochmal anders:

Ist der RegExp genügsam (non-greedy, mit Fragezeichen), dann begnügt er sich mit dem ersten ">, dass er findet und alles von "Die Heilige Suche" bis "Meso" landet in der 1. Gruppenklammer. Das ist das, was Du nicht willst, wenn ich das richtig verstanden habe.

Ist der RegExp gierig (greedy, ohne Fragezeichen), dann ist er erst mit dem letztmöglichen "> zufrieden, und das ist das direkt vorm "Meso" und nur "Meso" landet dann in der 1. Gruppenklammer.


BTW: Wenn die 3 Werte immer am Ende des Strings stehen, würde ich den RegExp eher am Ende des Strings 'verankern' (mit $):
p=re.compile(r'([^>]+)</a> \(([\dA-Z:]+?)\)</td><td>([\d\.]+?)</td>$',re.I)
(nicht optimiert).

Jan
Gast

Voges hat geschrieben:Ist der RegExp genügsam (non-greedy, mit Fragezeichen), dann begnügt er sich mit dem ersten ">, dass er findet und alles von "Die Heilige Suche" bis "Meso" landet in der 1. Gruppenklammer. Das ist das, was Du nicht willst, wenn ich das richtig verstanden habe.
Da ist genau mein Problem, und auch das warum wir und missverstehen... ist mir aber erst jetzt aufgefallen, weil ich wieder was vergessen hatte :oops: . Hinter dem einem Ausdruck stehen noch andere, ich versuch das mal in einem Beispiel, indem ich den Suchstring erweitere.

Code: Alles auswählen

text="""<td><a href="/cgi-bin/info.pl?sid=EEDFMVQFIEXGQDTR&alli=R42">Die Heilige Suche</a> [R42]</td></tr><tr><td>Metaallianz</td><td><a href="/cgi-bin/info.pl?sid=EEDFMVQFIEXGQDTR&meta=FAA">Foederation Auserwaehlter Allianzen</a> [FAA]</td></tr><tr><th colspan=2>Planeten (insgesamt 31)</th></tr><tr><td>Name (Koordinaten)</td><td>Punkte</td></tr><tr><td><a href="/cgi-bin/info.pl?sid=EEDFMVQFIEXGQDTR&coord=555763A">Meso</a> (555:763:A)</td><td>253</td></tr><tr><td><a href="/cgi-bin/info.pl?sid=EEDFMVQFIEXGQDTR&coord=550763A">Nummer 2</a> (550:763:A)</td><td>22</td></tr>"""
Jetzt (ich hol nochmal ganz vom Anfang an aus) kommt mein Problem, dass er "zuviel" zurückgibt, aber ich nicht verstehe warum. Nun liefert er nämlich das (wobei der letztere Wert wieder ok ist):

Code: Alles auswählen

('Die Heilige Suche</a> [R42]</td></tr><tr><td>Metaallianz</td><td><a href="/cgi-bin/info.pl?sid=EEDFMVQFIEXGQDTR&meta=FAA">Foederation Auserwaehlter Allianzen</a> [FAA]</td></tr><tr><th colspan=2>Planeten (insgesamt 31)</th></tr><tr><td>Name (Koordinaten)</td><td>Punkte</td></tr><tr><td><a href="/cgi-bin/info.pl?sid=EEDFMVQFIEXGQDTR&coord=555763A">Meso', '555:763:A', '253'), ("Nummer 2", "550:763:A","22")]
Weil mehrere dieser Werte hintereinanderstehen können, möchte ich nicht einfach ein "*" benutzten. Scheinbar hab ich etwas in meinem RE zweideutig ausgelegt, diese Stelle finde ich jedoch nicht. Mit "(.+?)</a> \(([\dA-Z:]+?)\)" sollte ja eigentlich der Name "Meso" und die Koordinaten "555:763:A" gefangen werden. In der Anwendung übersieht er aber das "</a>" in Verbindung mit dem "+?" in diesem Teil. Denn wie man im Ergebniss sieht, ist da ja "Die Heilige Suche</a>" das eigentliche Schlussglied drin. Hier sollte er mit dem ersten Gruppenteil aufhöhren, und versuchen den 2. zu finden. Diesen kann er meiner Meinung nach aber nicht finden, weil eigentlich die Runden Klammern aus "\(([\dA-Z:]+?)\)" nicht vorhanden sind (zum Vergelich die Stelle die ich meine: "Die Heilige Suche</a> [R42]"). Tja, und wenn er das nicht finden kann, sollte er weiter unten sein Glück versuchen und dann in """<td><a href="/cgi-bin/info.pl?sid=EEDFMVQFIEXGQDTR&coord=555763A">Meso</a> (555:763:A)</td><td>253</td>""" Erfolg haben. Ich hoffe, du verstehst nun was ich meine ;). Was hab ich falsch gemacht?
Voges
User
Beiträge: 564
Registriert: Dienstag 6. August 2002, 14:52
Wohnort: Region Hannover

Hallo!
Anonymous hat geschrieben:Diesen kann er meiner Meinung nach aber nicht finden, weil eigentlich die Runden Klammern aus "\(([\dA-Z:]+?)\)" nicht vorhanden sind (zum Vergelich die Stelle die ich meine: "Die Heilige Suche</a> [R42]"). Tja, und wenn er das nicht finden kann, sollte er weiter unten sein Glück versuchen
Wenn der Parser "</a> [" findet, dann weiß er, dass die Zeichen dieser Zeichenkette zu den beliebigen Zeichen .+? gehören müssen, denn er sucht ja nicht nach "</a> [" sondern nach "</a> (".

Nochmal:
<td><a href="/cgi-bin/info\.pl.*?"> erkennt erstmal alles bis vor das "Die Heilige". Da non-greedy, belässt es der Parser erstmal dabei. Ok soweit?
(.+?)</a> \( versucht nun eine beliebige Anzahl von beliebigen Zeichen zu erkennen bis die Zeichenfolge "</a> (" erkannt wird. Alles ab "Die Heilige" bis einschließlich "Meso" sind doch beliebige Zeichen auf die eben "</a> (" folgt und somit ist der RegExp erfüllt und alles ab "Die Heilige" bis "Meso" landet in der 1. Gruppenklammer. Das da mal irgendwo zwischendurch zweimal die Zeichenfolge "</a> [" auftaucht, ist ohne Belang, denn für diese Zeichenfolge interessiert sich der RegExp ja gar nicht.
Lass es erstmal sacken. Vielleicht wird das irgendwann mal klarer.

Jan
Gast

Voges hat geschrieben:Hallo!
Anonymous hat geschrieben:Diesen kann er meiner Meinung nach aber nicht finden, weil eigentlich die Runden Klammern aus "\(([\dA-Z:]+?)\)" nicht vorhanden sind (zum Vergelich die Stelle die ich meine: "Die Heilige Suche</a> [R42]"). Tja, und wenn er das nicht finden kann, sollte er weiter unten sein Glück versuchen
Wenn der Parser "</a> [" findet, dann weiß er, dass die Zeichen dieser Zeichenkette zu den beliebigen Zeichen .+? gehören müssen, denn er sucht ja nicht nach "</a> [" sondern nach "</a> (".
:idea: Hmm, das leuchtet mir ein... Dann müsste ich also noch weitere Merkmale mit reinpacken, damit er diesen Nebeneffeckt nicht zulässt? Naja, danke erstmal, dass du mir hier das Verhalten des Parsers hier erklärt hast, ich hab schon gedacht ich kapier gar nix mehr... :roll: :wink:
Gast

jaja, die Dinge gehen einfach, wenn man sie richtig versteht... ich hab ne Lösung, also vielen Dank @ Voges. :D

Code: Alles auswählen

p=re.compile('<td><a href="/cgi-bin/info\.pl.*?coord=[\dA-Z]+?">(.+?)</a> \(([\dA-Z:]+?)\)</td><td>([\d\.]+?)</td>',re.I)
Voges
User
Beiträge: 564
Registriert: Dienstag 6. August 2002, 14:52
Wohnort: Region Hannover

Anonymous hat geschrieben:

Code: Alles auswählen

p=re.compile('<td><a href="/cgi-bin/info\.pl.*?coord=[\dA-Z]+?">(.+?)</a> \(([\dA-Z:]+?)\)</td><td>([\d\.]+?)</td>',re.I)
Folgendes wollte ich gerade abschicken ;-) :
Wäre es nicht eindeutig, wenn Du nach "(<Ziffern>:<Ziffern>:<Buchstabe(n)>)" suchtest und nach den Werten links und rechts davon? In etwa so:
p=re.compile(r'coord=[\dA-Z]+">([^>]+?)</a> \((\d+:\d+:\w+)\)</td><td>([\d.]+?)</td>',re.I)


Jan
Antworten