Regex: Warum gibt ein Match auf eine Oder-Verknüpfung von Gruppen Tupel zurück?

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
joma66
User
Beiträge: 5
Registriert: Dienstag 16. November 2021, 05:10

Hallo,

wie der Betreff schon sagt, verstehe ich nicht, warum einerseits

Code: Alles auswählen

re.findall('a|b', 'a')

Code: Alles auswählen

['a'] 
zurückgibt (wie erwartet).

Aber

Code: Alles auswählen

re.findall('(a)|(b)', 'a')

Code: Alles auswählen

[('a',  '')]
zurückgibt, also noch einen Leerstring.

Ich hab das jetzt auf den einfachsten Fall eingedampft, verhält sich aber analog bei beliebig komplizierten Ausdrücken.

Kann mir das jemand erklären? Woher kommt der Leerstring?

Dank und Gruß
joma
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du bekommst eben pro Gruppe einen match. Wenn du das nicht willst, kann man die Gruppe parametrisieren, und rein als Klammer setzen. Musst du in der Doku nachschlagen.
Benutzeravatar
DeaD_EyE
User
Beiträge: 1244
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Vielleicht denke ich jetzt zu einfach, aber wieso packst du das nicht in eine Gruppe?

Code: Alles auswählen

re.findall('(a|b)', 'a')
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Sirius3
User
Beiträge: 18278
Registriert: Sonntag 21. Oktober 2012, 17:20

Das hängt halt an Deiner Erwartung. Meine Erwartung ist, dass sich die Funktion so verhält wie in der Dokumentation beschrieben:
The result depends on the number of capturing groups in the pattern. If there are no groups, return a list of strings matching the whole pattern. If there is exactly one group, return a list of strings matching that group. If multiple groups are present, return a list of tuples of strings matching the groups. Non-capturing groups do not affect the form of the result.
joma66
User
Beiträge: 5
Registriert: Dienstag 16. November 2021, 05:10

Sirius3 hat geschrieben: Dienstag 16. November 2021, 10:38 Das hängt halt an Deiner Erwartung. Meine Erwartung ist, dass sich die Funktion so verhält wie in der Dokumentation beschrieben:
The result depends on the number of capturing groups in the pattern. If there are no groups, return a list of strings matching the whole pattern. If there is exactly one group, return a list of strings matching that group. If multiple groups are present, return a list of tuples of strings matching the groups. Non-capturing groups do not affect the form of the result.
Meine Erwartung war, dass, gemäß der Doku zu Gruppen, diese zur optischen Gliederung dienen, oder um auf Teile der regex woanders zugreifen zu können. Nicht, dass sie das Ergebnis und dessen Form beeinflussen.
Dank Deinem Hinweis auf die Doku zu re.findall, seh ich jetzt, dass das anders ist :idea:

Es bleiben aber noch Fragen:

1. Das durchbricht doch den regelmäßigen Gebrauch von Gruppen , hat das irgendeinen besonderen Zweck? Oder anders gefragt: warum möchte man da Tupel zurückbekommen?

2.
Non-capturing groups do not affect the form of the result
In meinem Beispiel beeinflusst das Non-capturing (b) durchaus das Resultat, und weitere nichtmatchende Gruppen ebenso:

Code: Alles auswählen

re.findall('(a)|(b)(c)', 'a')
[('a', '', '')}
Die Tupel wachsen mit jeder Gruppe, egal ob sie matcht oder nicht. (Non-capturing = matcht nicht, oder?)

3. müsste doch nach der Systematik, wenn keine Gruppe matcht, das Ergebnis rauskommen:

Code: Alles auswählen

re.findall('(a)|(b)', 'x')
[('', '')}
Und nicht nur Also ich versteh weder den Zweck der (Ausnahme)regel, noch erkenne ich, dass das Ergebnis der Doku entspricht.
Vielleicht mag mir noch jemand auf die Sprünge helfen.

Dank und Gruß
joma
joma66
User
Beiträge: 5
Registriert: Dienstag 16. November 2021, 05:10

DeaD_EyE hat geschrieben: Dienstag 16. November 2021, 09:58 Vielleicht denke ich jetzt zu einfach, aber wieso packst du das nicht in eine Gruppe?

Code: Alles auswählen

re.findall('(a|b)', 'a')
Ist mir klar, dass man es anders machen kann.
Mir gehts aber darum, das Verhalten zu verstehen, wenn man es so macht; was es mit dieser Tupel-Rückgabe auf sich hat.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Gruppen sind halt nicht so definiert, wie du glaubst. Sie sind im Normalfall dazu da, auf Teilausdrücke zuzugreifen. Man kann das unterdrücken, muss man aber explizit machen. Ist halt so rum entschieden worden.
Benutzeravatar
__blackjack__
User
Beiträge: 14076
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@joma66: Ein Problem beim Verständnis ist wohl „Non-capturing = matcht“ — denn nein „capturing“ ist was anderes als „matchen“. Du hast da zwei „capturing“-Gruppen. Ob die matchen oder nicht ist erst einmal egal. Sobald irgendeine eine matcht, werden alle „capturing“-Gruppen zurückgegeben, auch die leeren. Auf die Weise kannst Du zum Beispiel (nicht besonders übersichtlich) feststellen *welche* Gruppe den Treffer ausgelöst hat. Das will man manchmal wissen, weil dann jeweils etwas anderes passieren soll.

Wenn einem das egal ist welche Gruppe das wahr, dann wurde ja schon geschrieben wie man das löst: man formuliert das so, dass es nur eine Gruppe gibt: "(a|b)".

Wenn keine Gruppe matched, dann gibt es eine leere Liste, weil es sonst aufwändiger wäre das nochmal festzustellen. Das hat das `re`-Modul ja bereits festgestellt, warum sollte man dann noch mal jedes Ergebnis untersuchen müssen ob es ein match war oder nicht? Und vor allem: Wie viele Ergebnisse soll das denn geben? Denn sehr viele Ausdrücke matchen ja an sehr vielen Stellen nicht. Dann hast Du am Ende hunderte Tupel in der Liste die alle leere Gruppenergebnisse haben.

Wenn Du Gruppen nur dazu hast um, nun ja Alternativen zu Gruppieren, Dich aber der Inhalt der Gruppe bei einem Match nicht interessiert, dann mach da halt eine „non capturing“-Gruppe draus, dann taucht die auch nicht im Ergebnis auf.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Die Gruppe 0 ist ja eh immer der gesamte Match. Darum ist das ja auch im Grunde egal, wenn alle anderen kommen.
joma66
User
Beiträge: 5
Registriert: Dienstag 16. November 2021, 05:10

Ok, Dankeschön für die die vielen Antworten und Anregungen!
Ich denke ein Problem war meine Cature/Match-Konfusion.
Letztlich, wie einige ja auch zurecht sagten, muss ich mich einfach dran gewöhnen, das es ist wie es ist.
Sirius3
User
Beiträge: 18278
Registriert: Sonntag 21. Oktober 2012, 17:20

@joma66: was ist denn Dein Problem, das Du hier zu lösen versuchst?
joma66
User
Beiträge: 5
Registriert: Dienstag 16. November 2021, 05:10

Sirius3 hat geschrieben: Mittwoch 17. November 2021, 08:12 @joma66: was ist denn Dein Problem, das Du hier zu lösen versuchst?
Allgemein versuch ich als Neuling Python-Konzepte zu verstehen..

Speziell bin ich bei der Musterlösung einer Programmieraufgabe auf solche Ausgaben einer regex gestoßen, die sehr ausgiebig und komplex von Gruppen gebrauch macht::

Code: Alles auswählen

[('', '', '', '', '', '', 'y'),
 ('', '', '', '', '', ' ', ''),
 ('', '', '', '=', '', '', ''),
 ('', '', '', '', '', ' ', ''),
 ('5', '', '', '', '', '', ''),
 ('', '', '', '', '', ' ', ''),
 ('', '', '', '*', '', '', ''),
 ('', '', '', '', '', ' ', ''),
 ('', '', '', '', '', '', 'x')]
Mir gings drum, ob da ein bug drin ist, oder diese Ausgabe ein Feature ist.
Das hat sich ja geklärt und ich ahne so langsam den Sinn von diesen Tupeln .
Also mein Problem war vor allem der Knoten in meinem Kopf. Wie das so ist, wenn man mit etwas anfängt :)
Für mich ist die Sache soweit erledigt- Danke nochmal!
Antworten