[Gelöst] Frage zu non-overlapping matches.

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
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

finditer( pattern, string[, flags])
Return an iterator over all non-overlapping matches for the RE pattern in string. For each match, the iterator returns a match object. Empty matches are included in the result unless they touch the beginning of another match. New in version 2.2. Changed in version 2.4: Added the optional flags argument.
Vorweg:
Wie ist es eigentlich mit der Reihenfolge der Auswertung von regex?
Beispiel 1: http://paste.pocoo.org/show/960/

Soweit Ok. (?P<_1>\*\*) greift vor (?P<_2>\*)

Ausgabe:

Code: Alles auswählen

[('_1', '**')]
[('_1', '**')]
[('_2', '*')]
[('_2', '*')]
Beispiel 2: http://paste.pocoo.org/show/961/
Vertausche ich die Reihenfolge kommt wie erwarte folgendes, da ** nie erkannt werden kann, weil * davor liegt.

Code: Alles auswählen

[('_2', '*')]
[('_2', '*')]
[('_2', '*')]
[('_2', '*')]
[('_2', '*')]
[('_2', '*')]
So weit Ok. Die Reihenfolge wird also erkannt und re hält sich daran auch.

Um so verblüffender folgendes, das cih nicht verstehen kann. Und da kommt nun non-overlapping in Spiel, falls ich das richtig verstehe.

Beispiel 3: http://paste.pocoo.org/show/962/
Wie erwartet folgende Ausgabe:

Code: Alles auswählen

[('linebreak', '\n')]
[('command', '#link'), ('arg', 'wwwt.test.de')]
[('linebreak', '\n')]
[('bold', '**')]
[('bold', '**')]
[('linebreak', '\n')]
[('linebreak', '\n')]
Beispiel 4: http://paste.pocoo.org/show/963/
Folgende Ausgab hätte ich nicht erwartet und verstehe nicht, warum sich re da nicht an die Reihenfolge hält?!

Code: Alles auswählen

[('linebreak', '\n')]
[('literal', '    #link[wwwt.test.de]')]
[('linebreak', '\n')]
[('literal', '    test...! **test**')]
[('linebreak', '\n')]
[('literal', '    blubb')]
[('linebreak', '\n')]
[('literal', '    ')]
(?P<literal>.*) ist als letzter ausdruck und sollte auch als letztes ausgewertet werden, wenn alle davorliegenden nicht greifen. Warum hält sich re nicht daran und ist das mit non-overlapping gemeint? Wenn das mit non-overlapping gemeint ist, warum überlappt sich dann nicht auch (\*\*)|(\*)? Weil wenn die Reihenfolge kein rolle spielt, würde (\*) immer den den Vorzug vor (\*\*) gegeben werden. Irgendwas stimmt da IMHO in der Umsetzung nicht, ist Paradox und inkonsistent.

Weiß einer eine Antwort und ein Rat? -- Ich möchte nun ungern mit ``span``, Positionspeicherungen und Indexzugriffen arbeiten um literale rauszufischen. Das ist sehr fehleranfällig und macht den Code nur unnötig kompliziert.

lg
Zuletzt geändert von sape am Dienstag 13. Februar 2007, 23:19, insgesamt 1-mal geändert.
BlackJack

sape hat geschrieben:(?P<literal>.*) ist als letzter ausdruck und sollte auch als letztes ausgewertet werden, wenn alle davorliegenden nicht greifen.
Genau das passiert doch. Das ist die einzige Alternative, die auf den Anfang der Zeichenkette zutrifft. Und es werden davon alle Zeichen bis zum Ende erfasst.

Das hier ist die Zeichenkette:

Code: Alles auswählen

"    #link[wwwt.test.de]"
Gehen wir die Alternativen durch:

1. Fängt sie mit einem "*" an? Nein.
2. Fängt sie mit einem "{" an? Nein.
3. Fängt sie mit einem "\r" oder "\n" an? Nein.
4. Fängt sie mit einem "#" an? Nein.
5. Letzte Regel: Fängt sie mit *irgendeinem* Zeichen an? Ja! Und dann so viele beliebige Zeichen wie möglich, also alles.
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Erstmal danke für den Tipp. Werde ich nachehr mal was austesten.

EDIT:
5. Letzte Regel: Fängt sie mit *irgendeinem* Zeichen an? Ja! Und dann so viele beliebige Zeichen wie möglich, also alles.
Ach mist, nun hat es klick gemacht :? Danke für den Tipp!
lg
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

BlackJack, auf ne Lösung bin ich aber nicht gekommen. Ist das überhaupt machbar, was ich will? Momentan mache ich das doch mit ``span`` um die Text stellen rauszufischen, was ich aber nicht so elegant finde.
BlackJack

Du brauchst einen regulären Ausdruck, der alles erfasst ausser das, was der Ausdruck matcht den Du schon hast. Reguläre Ausdrücke sind zwar unter Komplementbildung abgeschlossen, also grundsätzlich geht das, aber das Ergebnis so eines negierten regulären Ausdrucks möchte ich nicht wirklich per Hand "berechnen". Und es sieht wahrscheinlich auch nicht so toll aus.
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Oh. Das Möchte ich auch nicht gerne von Hand berechen, da es noch komplizierter ist.

Danke für deine Tipps und Erklärungen.

lg

P.S.: Ich bleibe dann bei der ``span``-Variante, da es doch am leichtesten und wohl auch am schnellsten ist.

EDIT2: Korrektur.
Benutzeravatar
Luzandro
User
Beiträge: 87
Registriert: Freitag 21. April 2006, 17:03

Machbar ja, sinnvoll in meiner Meinung nach nicht - dein voriger Ausdruck sucht den längsten Match, der _irgendetwas_ findet, also einfach alles. Was du willst ist der kürzeste Teil, der _nicht_ deine anderen Teile matcht - solche negativen Geschichten als regex ausdrücken ist etwas mühsam, aber in deinem Fall ja sowieso unnötig: du gehst die Matches sowieso sequentiell durch und weißt mit m.start()/m.end() wann dein letzter Match war und ob daher ein Literal dazwischen war.

Einen ähnlichen Fehler wie mit dem Literal hast du übrigens auch bei link/image:

Code: Alles auswählen

"((?P<command>\#link|\#image)\[(?P<arg>.+)\])"
Hier matcht du auch nach einem "[" _alles_ mit einem anchließenden "]" - das schließt allerdings auch andere schließende Klammern dazwischen ein, d.h. du willst eigentlich

Code: Alles auswählen

"((?P<command>\#link|\#image)\[(?P<arg>[^\]]+)\])"
Dein

Code: Alles auswählen

for line in re.split(r'(\r?\n)', src):
verstehe ich auch nicht ganz, da diese regex dann auch nochmal eine Regel in deinem Pattern ist
[url=http://www.leckse.net/artikel/meta/profilieren]Profilieren im Netz leicht gemacht[/url]
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Luzandro hat geschrieben:du gehst die Matches sowieso sequentiell durch und weißt mit m.start()/m.end() wann dein letzter Match war und ob daher ein Literal dazwischen war.
So mache ich das ja auch jetzt. Ist aber wie gesagt ein wenig "aufwändig", da die Textstellen sowohl davor, als auch zwischen dem matches, als auch dahinter sein können. Das wollte ich mir halt gerne sparen. Aber nun habe ich das ehe gemacht.
Luzandro hat geschrieben: Einen ähnlichen Fehler wie mit dem Literal hast du übrigens auch bei link/image:

Code: Alles auswählen

"((?P<command>\#link|\#image)\[(?P<arg>.+)\])"
Hier matcht du auch nach einem "[" _alles_ mit einem anchließenden "]" - das schließt allerdings auch andere schließende Klammern dazwischen ein, d.h. du willst eigentlich

Code: Alles auswählen

"((?P<command>\#link|\#image)\[(?P<arg>[^\]]+)\])"
Ups, stimmt. Hier ist es angebracht [] zu negieren.

Luzandro hat geschrieben: Dein

Code: Alles auswählen

for line in re.split(r'(\r?\n)', src):
verstehe ich auch nicht ganz, da diese regex dann auch nochmal eine Regel in deinem Pattern ist
Die haben aber beide nichts miteinader zu tun. Das ``re.split(r'(\r?\n)', src)`` dient nur dazu um aus dem string eine Liste zu erzeugen, bei der aber die \n und/oder \r\n nicht wegfallen, sondern mit in die Liste gepackt werden. Danach wird über die einzelnen Zeilen der Liste iteriert.

Die eigentliche tokenizierung übernimmt ``for m in tags_re.finditer(line):`` und da möchte ich auch linebreaks erfassen, weil die z.B. bei dauCMS-Wiki-Syntax benötigt werden. Z.B. ist die dauCMS-Wiki-Syntax so konzipiert, das überall wo ein Linebreak ist, auch tatsächlich ein Lineberak im XHTML-Output erzeugt wird (</ br>) im Gegensatz zu anderen Wiki-Syntaxen.
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

sape hat geschrieben:
Luzandro hat geschrieben: Einen ähnlichen Fehler wie mit dem Literal hast du übrigens auch bei link/image:

Code: Alles auswählen

"((?P<command>\#link|\#image)\[(?P<arg>.+)\])"
Hier matcht du auch nach einem "[" _alles_ mit einem anchließenden "]" - das schließt allerdings auch andere schließende Klammern dazwischen ein, d.h. du willst eigentlich

Code: Alles auswählen

"((?P<command>\#link|\#image)\[(?P<arg>[^\]]+)\])"
Ups, stimmt. Hier ist es angebracht [] zu negieren.
Besser lesbar ist es evtl., einfach ein non-greedy + zu verwenden, also "+?".
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
Antworten