Python nicht gierige Regexe

150

Wie erstelle ich eine Python-Regex wie "(.*)"diese, wenn "a (b) c (d) e"Python-Übereinstimmungen "b"statt "b) c (d"?

Ich weiß, dass ich "[^)]"anstelle von verwenden kann ".", aber ich suche nach einer allgemeineren Lösung, die meine Regex ein wenig sauberer hält. Gibt es eine Möglichkeit, Python zu sagen "Hey, passe das so schnell wie möglich an"?

So8res
quelle

Antworten:

207

Du suchst die Allmächtigen *?

Aus den Dokumenten, Gierig gegen Nicht-Gierig

die nicht-gierigen Qualifikations *?, +?, ??, oder {m,n}?[...] Spiel als wenig Text wie möglich.

Trey Stout
quelle
Laut dem Internetarchiv war alles, worauf auf diesen Link verwiesen wurde, eine Kopie der Python "re" -Modul-Dokumente, sodass Treys Link genauso gut funktioniert.
Spiffytech
2
Was ist der gebräuchliche englische Name dafür *??
Trevor Boyd Smith
Platzhalterzeichen @Trevor Boyd Smith
Serge
3
Dies wird als "nicht
gieriges
65
>>> x = "a (b) c (d) e"
>>> re.search(r"\(.*\)", x).group()
'(b) c (d)'
>>> re.search(r"\(.*?\)", x).group()
'(b)'

Laut den Dokumenten :

Die Qualifikanten ' *', ' +' und ' ?' sind alle gierig; Sie passen so viel Text wie möglich zusammen. Manchmal ist dieses Verhalten nicht erwünscht. Wenn der RE <.*>mit ' <H1>title</H1>' abgeglichen wird, stimmt er mit dem gesamten String überein und nicht nur mit ' <H1>'. Durch Hinzufügen von ' ?' nach dem Qualifikationsspiel wird das Match nicht gierig oder minimal ausgeführt. Es werden so wenig Zeichen wie möglich gefunden. Die Verwendung .*?im vorherigen Ausdruck stimmt nur mit ' <H1>' überein .

Paolo Bergantino
quelle
14

Würde nicht \\(.*?\\)funktionieren? Das ist die nicht gierige Syntax.

Zitrax
quelle
5

Wie die anderen gesagt haben mit dem? Der Modifikator auf dem * Quantifizierer löst Ihr unmittelbares Problem, aber seien Sie vorsichtig, Sie beginnen, in Bereiche zu verirren, in denen Regexe nicht mehr funktionieren und Sie stattdessen einen Parser benötigen. Zum Beispiel wird die Zeichenfolge "(foo (bar)) baz" Probleme verursachen.

Chas. Owens
quelle
5

Die Verwendung eines ungreedy Matchs ist ein guter Anfang, aber ich würde auch vorschlagen, dass Sie jede Verwendung von .*- was ist damit?

groups = re.search(r"\([^)]*\)", x)
ojrac
quelle
3

Möchten Sie, dass es mit "(b)" übereinstimmt? Tun Sie, was Zitrax und Paolo vorgeschlagen haben. Möchten Sie, dass es mit "b" übereinstimmt? Machen

>>> x = "a (b) c (d) e"
>>> re.search(r"\((.*?)\)", x).group(1)
'b'
David Berger
quelle
0

Zunächst empfehle ich nicht, "*" in regulären Ausdrücken zu verwenden. Ja, ich weiß, es ist das am häufigsten verwendete Trennzeichen für mehrere Zeichen, aber es ist trotzdem eine schlechte Idee. Dies liegt daran, dass "any" zwar eine beliebige Anzahl von Wiederholungen für dieses Zeichen aufweist, "any" jedoch 0 enthält. Dies ist normalerweise etwas, für das Sie einen Syntaxfehler auslösen möchten, das Sie nicht akzeptieren möchten. Stattdessen schlage ich vor, das +Zeichen zu verwenden, das mit jeder Wiederholung von Längen> 1 übereinstimmt. Außerdem haben Sie, soweit ich sehen kann, mit Ausdrücken in Klammern fester Länge zu tun. Infolgedessen können Sie wahrscheinlich die {x, y}Syntax verwenden, um die gewünschte Länge spezifisch anzugeben.

Wenn Sie jedoch wirklich eine nicht gierige Wiederholung benötigen, empfehle ich Ihnen, den Allmächtigen zu konsultieren ?. Wenn dies nach dem Ende eines Regex-Wiederholungsspezifizierers platziert wird, wird dieser Teil des Regex gezwungen, die geringstmögliche Textmenge zu finden.

Davon abgesehen würde ich sehr vorsichtig mit dem sein, ?da es, wie der Sonic Screwdriver in Dr. Who, die Tendenz hat, "leicht" unerwünschte Dinge zu tun, wenn es nicht sorgfältig kalibriert wird. Wenn Sie beispielsweise Ihre Beispieleingabe verwenden möchten, wird diese ((1)als Übereinstimmung identifiziert (beachten Sie das Fehlen eines zweiten Rparens).

Die Daleks
quelle