Regular Expressions: Gruppierung klappt nicht

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
Apotekarnes
User
Beiträge: 18
Registriert: Mittwoch 21. September 2011, 10:02

Hallo allerseits,

ich bin ein Neuling mit Regular Expressions in Python.

Aus dem String:
",,,,...,..,.,.G.,....GGG,.,.G.GGGGG.GG.GG..GGGGGG,GGGG.GGGGG^!G^!G^~G^!G^~G^!G^!g^!"
Ich möchte alle Wörter gelistet bekommen, die den folgenden Regeln entsprechen.

Einzel-Zeichen aus der Menge [,.Gg] mit optionalem (null-mal oder einmal) Präfix der Länge 2 der Form:
[^$].
das heißt ein gültiges Präfix muss mit entweder ^ oder $ beginnen und dann mit einem beliebigen Zeichen weitergehen.

Gültige Beispiele sind also:

"g", "G", ",", ".", "^mG" oder "$cg"

ungültig ist ",xG"

Mein Versuch, eine Regular Expression dafür zu konstruieren ergab:

r'([^$].)?[,.Gg]'

Mit den runden Klammern wollte ich gruppieren, d.h. regulieren, dass sich das ? auf beide Zeichen des Präfix bezieht.

Die Suche ergibt dann leider:

>>> regex.findall(string)
[u',,', u',.', u'.,', u'.,', u',.', u'.,', u'..', u'GG', u',.', u'.G', u'GG', u'GG', u'GG', u'GG', u'.G', u'GG', u'G,', u'GG', u'.G', u'GG', u'^!', u'^!', u'^~', u'^!', u'^~', u'^!', u'^!']

Was mache ich falsch?
Kann jemand helfen?

Viele Grüße
Apotekarnes
BlackJack

@Apotekarnes: '[^$]' bedeutet ein Zeichen was alles sein kann *ausser* ein '$'. '^' als erstes Zeichen in '[]' ist die Negation der folgenden Zeichen. Versuchs mal mit '[$^]' für '$' oder '^'.
Apotekarnes
User
Beiträge: 18
Registriert: Mittwoch 21. September 2011, 10:02

@ BlackJack: Leider klappt das auch nicht:

([$^].)?[,.Gg]
liefert

[u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'', u'^!', u'^!', u'^~', u'^!', u'^~', u'^!', u'^!']

Wo kommen denn diese ganzen Leerstrings her?
BlackJack

@Apotekarnes: Die leeren Zeichenketten kommen dadurch zustande, dass das Muster insgesamt auf viele Stellen zutrifft wo zwar die zweite Zeichenmenge matcht, aber die optionale Gruppe nicht. Und in so einem Treffer des Musters ergibt die erste Gruppe die Leerzeichenkette als Ergebnis. Das kann man an einer ganz einfachen Eingabe schon sehen:

Code: Alles auswählen

In [9]: re.findall(r'([$^].)?[,.Gg]', ',')
Out[9]: ['']
Die optionale Gruppe enthält nichts, das Komma aus der zweiten Zeichenmenge trifft zu, damit matcht der gesamte reguläre Ausdruck, und als Ergebnis wird dann der Inhalt der Gruppe genommen — nichts, also eine leere Zeichenkette. Was hättest Du den in diesem Fall zum Beispiel erwartet?

Wenn es das Komma hätte sein sollen, dann muss man zwei Veränderungen vornehmen.

1. Muss der gesamte Ausdruck in Klammern gesetzt werden, damit der gesamte Ausdruck eine Gruppe ist, die im Ergebnis auftaucht.

2. Dann bekommt man Tupel mit zwei Ergebnissen pro Treffer, nämlich ein Ergebnis pro Gruppe. Wenn man das nicht möchte muss man aus der alten Gruppe eine machen die nur gruppiert, aber sich nichts „merkt”.

Die beiden Schritte als Code:

Code: Alles auswählen

In [10]: re.findall(r'(([$^].)?[,.Gg])', ',')
Out[10]: [(',', '')]

In [11]: re.findall(r'((?:[$^].)?[,.Gg])', ',')
Out[11]: [',']
Reguläre Ausdrücke sind eine spassige Angelegenheit. :-)
Apotekarnes
User
Beiträge: 18
Registriert: Mittwoch 21. September 2011, 10:02

Vielen Dank, BlackJack!
Das war sehr hilfreich!
Genau so habe ich es gemeint :)

Ich finde die Syntax trotzdem recht unintuitiv und bin da etwas enttäuscht von REs in Python. Hoffe, das ist in anderen Sprachen mehr straight-forward geregelt. Im Studium haben wir das so gelernt, wie ich es zuerst probiert hatte.
BlackJack

@Apotekarnes: Nein, das ist in anderen Sprachen nicht wirklich anders geregelt. Ich denke da kommen im Gegensatz zur theoretischen Informatik einfach noch die praktischen Anforderungen und Probleme hinzu die irgendwie geregelt werden müssen. Zum Beispiel gibt es in der Theorie nicht das Problem zwischen Gruppen-Klammern unterscheiden zu müssen die ihren Inhalt als Ergebnis aufbewahren und solchen die nur eine Gruppe markieren. Diese Unterscheidung will man in der Praxis haben, weil das sehr nützlich ist wenn man bestimmte Teile eines Treffers extrahieren möchte. Denn oft stehen die interessanten Teile zwischen irgendwelcher Syntax und ”Füllworten” die man zwar matchen muss, die aber für das Ergebnis uninteressant sind.

Und das `findall()` von jedem Treffer alle Gruppeninhalte liefert wenn Gruppen im Ausdruck vorkommen ist eine sehr praktische Sache, denn genau dazu definiert man Gruppen ja. Sonderfall ist wenn keine Gruppen (mit „Gedächtnis”) definiert sind, dann liefert `findall()` als Ergebnis den gesamten Treffer. Man könnte in meinem Beispiel die äussere Gruppe auch weg lassen:

Code: Alles auswählen

In [12]: re.findall(r'(?:[$^].)?[,.Gg]', ',')
Out[12]: [',']
Antworten