Gruppen in sich wiederholenden Sub-Patterns

Hallo,
ich bin gerade auf etwas gestoßen, das mich ziemlich überrascht hat:
In [22]: import re
In [23]: regex = re.compile(r""" ...: ( ( \w+ , \w+ ) ) ...: (?: ...: , ( ( \w+ , \w+ ) ) ...: )* # beliebige Anzahl von Wiederholungen! ...: """, re.VERBOSE)
Hier geht es darum, in einem String alle durch Kommas getrennte Gruppen der Form '(ab,cd)' zu finden. (`\w+` kann natürlich mehr matchen, aber das sollte hier keinen Unterschied machen.)
Das Folgende funktioniert noch wie erwartet:
In [24]: regex.search('(ab,cd),(ef,gh)').groups() Out[24]: ('(ab,cd)', '(ef,gh)')
Aber das hat mich überrascht:
In [25]: regex.search('(ab,cd),(ef,gh),(ij,kl)').groups() Out[25]: ('(ab,cd)', '(ij,kl)')
In [26]: regex.search('(ab,cd),(ef,gh),(ij,kl),(mn,op)').groups() Out[26]: ('(ab,cd)', '(mn,op)')
Anscheinend findet sich im Match immer nur die letzte Gruppe aus dem mit `*` wiederholten Sub-Pattern.
_Erwartet_ hatte ich (aber _nicht_ bekommen!):
In [25]: regex.search('(ab,cd),(ef,gh),(ij,kl)').groups() Out[25]: ('(ab,cd)', '(ef,gh)', '(ij,kl)')
In [26]: regex.search('(ab,cd),(ef,gh),(ij,kl),(mn,op)').groups() Out[26]: ('(ab,cd)', '(ef,gh)', '(ij,kl)', '(mn,op)')
Kann man erklären, warum sich der Match anders als erwartet verhält (ohne nur das zu wiederholen, was ich schon gesagt habe ;-) )?
Gibt es eine Möglichkeit, den regulären Ausdruck so umzuschreiben, dass ich alle gewünschten Gruppen bekomme?
Falls nicht, wie würdet ihr das Problem sonst lösen? Mir sind mögliche Ansätze eingefallen, aber die wirken alle ziemlich frickelig.
Viele Grüße Stefan

Stefan Schwarzer wrote:
ich bin gerade auf etwas gestoßen, das mich ziemlich überrascht hat:
Anscheinend findet sich im Match immer nur die letzte Gruppe aus dem mit `*` wiederholten Sub-Pattern.
Kann man erklären, warum sich der Match anders als erwartet verhält (ohne nur das zu wiederholen, was ich schon gesagt habe ;-) )?
Nein, aber wenn ich es als Zitat der Dokumentation verpacke
""" If a group is contained in a part of the pattern that matched multiple times, the last match is returned. """
strahlt es doch hoffenlich die nötige Autorität aus ;)
Gibt es eine Möglichkeit, den regulären Ausdruck so umzuschreiben, dass ich alle gewünschten Gruppen bekomme?
Falls nicht, wie würdet ihr das Problem sonst lösen? Mir sind mögliche Ansätze eingefallen, aber die wirken alle ziemlich frickelig.
Das alternative regex-Modul von Matthew Barnett sammelt alle captures:
import regex m = regex.compile("(\w+( \w+)*)").search("ab cd ef") m.groups()
('ab cd ef', ' ef')
m.captures(2)
[' cd', ' ef']

On 16/11/2018 00.22, Peter Otten wrote:
Kann man erklären, warum sich der Match anders als erwartet verhält (ohne nur das zu wiederholen, was ich schon gesagt habe ;-) )?
Nein, aber wenn ich es als Zitat der Dokumentation verpacke
""" If a group is contained in a part of the pattern that matched multiple times, the last match is returned. """
strahlt es doch hoffenlich die nötige Autorität aus ;)
Ok, tut es. ;-) Das hatte ich dann wohl übersehen.
Falls nicht, wie würdet ihr das Problem sonst lösen? Mir sind mögliche Ansätze eingefallen, aber die wirken alle ziemlich frickelig.
Das alternative regex-Modul von Matthew Barnett sammelt alle captures:
import regex m = regex.compile("(\w+( \w+)*)").search("ab cd ef") m.groups()
('ab cd ef', ' ef')
m.captures(2)
[' cd', ' ef']
Interessant. Danke! :-)
Anscheinend gibt es das Modul schon einige Jahre, aber ich hatte nie davon gehört, vermutlich, weil für die meisten Anwendungen das `re`-Modul in der Standardbibliothek reicht.
Viele Grüße Stefan

Gibt es eine Möglichkeit, den regulären Ausdruck so umzuschreiben, dass ich alle gewünschten Gruppen bekomme?
Falls nicht, wie würdet ihr das Problem sonst lösen? Mir sind mögliche Ansätze eingefallen, aber die wirken alle ziemlich frickelig.
So würde ich es machen (Python 3.7):
import re regex = re.compile(r"""(\w+,\w+)""",
re.U).findall("""(ab,cd),(ef,gh),(ij,kl),(mn,op)""")
regex
['(ab,cd)', '(ef,gh)', '(ij,kl)', '(mn,op)']
regex = re.compile(r"""(\w+,\w+)""",
re.U).findall("""Anfang(ab,cd),(ef,gh),(ij,kl),(mn,op)(andereszeug)""")
regex
['(ab,cd)', '(ef,gh)', '(ij,kl)', '(mn,op)']
Viele Grüße

On 16/11/2018 08.25, Dustin Vanidestine wrote:
Gibt es eine Möglichkeit, den regulären Ausdruck so umzuschreiben, dass ich alle gewünschten Gruppen bekomme?
Falls nicht, wie würdet ihr das Problem sonst lösen? Mir sind mögliche Ansätze eingefallen, aber die wirken alle ziemlich frickelig.
So würde ich es machen (Python 3.7):
import re regex = re.compile(r"""(\w+,\w+)""",
re.U).findall("""(ab,cd),(ef,gh),(ij,kl),(mn,op)""")
regex
['(ab,cd)', '(ef,gh)', '(ij,kl)', '(mn,op)']
regex = re.compile(r"""(\w+,\w+)""",
re.U).findall("""Anfang(ab,cd),(ef,gh),(ij,kl),(mn,op)(andereszeug)""")
regex
['(ab,cd)', '(ef,gh)', '(ij,kl)', '(mn,op)']
Das Problem hierbei ist, dass die Kommas zwischen den eingeklammerten Gruppen Bestandteil der Syntax sind. Es soll zwischen den Gruppen nicht irgendetwas beliebiges stehen können.
Ansonsten wäre `findall` natürlich das Mittel der Wahl.
Viele Grüße Stefan
participants (3)
-
Dustin Vanidestine
-
Peter Otten
-
Stefan Schwarzer