Gierig gegen widerstrebend gegen besitzergreifende Quantifizierer

357

Ich fand dieses hervorragende Tutorial zu regulären Ausdrücken und obwohl ich intuitiv verstehe, was "gierige", "widerstrebende" und "besitzergreifende" Quantifizierer tun, scheint es eine ernsthafte Lücke in meinem Verständnis zu geben.

Im folgenden Beispiel:

Enter your regex: .*foo  // greedy quantifier
Enter input string to search: xfooxxxxxxfoo
I found the text "xfooxxxxxxfoo" starting at index 0 and ending at index 13.

Enter your regex: .*?foo  // reluctant quantifier
Enter input string to search: xfooxxxxxxfoo
I found the text "xfoo" starting at index 0 and ending at index 4.
I found the text "xxxxxxfoo" starting at index 4 and ending at index 13.

Enter your regex: .*+foo // possessive quantifier
Enter input string to search: xfooxxxxxxfoo
No match found.

In der Erklärung wird erwähnt , dass die gesamte Eingabezeichenfolge gegessen wurde , Buchstaben verbraucht wurden , der Matcher zurückgesetzt wurde , dass das Auftreten von "foo" ganz rechts wieder aufflammte usw.

Leider verstehe ich trotz der schönen Metaphern immer noch nicht, was von wem gegessen wird ... Kennen Sie ein anderes Tutorial, das (kurz) erklärt, wie Engines-Engines funktionieren?

Wenn jemand den folgenden Absatz in einer etwas anderen Formulierung erklären kann, wäre dies sehr dankbar:

Im ersten Beispiel wird der gierige Quantifizierer * verwendet, um "alles" null oder mehrmals zu finden, gefolgt von den Buchstaben "f" "o" "o". Da der Quantifizierer gierig ist, frisst der. * -Teil des Ausdrucks zuerst die gesamte Eingabezeichenfolge. Zu diesem Zeitpunkt kann der Gesamtausdruck nicht erfolgreich sein, da die letzten drei Buchstaben ("f" "o" "o") bereits verbraucht wurden ( von wem? ). Der Matcher zieht sich also langsam ( von rechts nach links? ) Buchstabe für Buchstabe zurück, bis das Auftreten von "foo" ganz rechts wieder aufgeflogen ist ( was bedeutet das? ). An diesem Punkt ist das Match erfolgreich und die Suche endet.

Das zweite Beispiel ist jedoch zurückhaltend, daher beginnt es damit, zuerst ( von wem? ) "Nichts" zu konsumieren . Da "foo" nicht am Anfang der Zeichenfolge erscheint, muss der erste Buchstabe (ein "x") geschluckt werden ( wer schluckt?), Was die erste Übereinstimmung bei 0 und 4 auslöst. Unser Testgeschirr setzt den Vorgang fort bis die Eingabezeichenfolge erschöpft ist. Es findet eine weitere Übereinstimmung bei 4 und 13.

Das dritte Beispiel findet keine Übereinstimmung, weil der Quantifizierer besitzergreifend ist. In diesem Fall wird die gesamte Eingabezeichenfolge von. * + ( Wie? ) Verbraucht, sodass nichts übrig bleibt, um das "foo" am Ende des Ausdrucks zu erfüllen. Verwenden Sie einen Possessivquantifizierer für Situationen, in denen Sie alles erfassen möchten, ohne sich jemals zurückzuziehen ( was bedeutet Zurücksetzen? ). In Fällen, in denen die Übereinstimmung nicht sofort gefunden wird, übertrifft sie den entsprechenden gierigen Quantifizierer.

Regex Rookie
quelle
22
Maximal quantifiers mögen *, +und ?sind gierig. Minimal quantifiers mögen *?, +?und ??sind faul. Possessive Quantoren mögen *+, ++und ?+sind klebrig.
Tchrist
6
Diese Frage wurde zu den häufig gestellten Fragen zum Stapelüberlauf für reguläre Ausdrücke unter "Quantifizierer> Weitere Informationen zu den Unterschieden ..." hinzugefügt .
Aliteralmind
Von Interesse: Die Java ™ -Tutorials - Unterschiede zwischen gierigen, widerstrebenden und besitzergreifenden Quantifizierern - Scrollen Sie nach unten, um den Abschnitt anzuzeigen .
Guy Coder

Antworten:

495

Ich versuche es mal.

Ein gieriger Quantifizierer passt zuerst so gut wie möglich zusammen. Das .*passt also zur gesamten Saite. Dann versucht der Matcher, fFolgendes zu finden, aber es sind keine Zeichen mehr vorhanden. Es wird also "zurückverfolgt", wodurch der gierige Quantifizierer mit einem Zeichen weniger übereinstimmt (wobei das "o" am Ende der Zeichenfolge nicht übereinstimmt). Das stimmt immer noch nicht mit dem fim regulären Ausdruck überein , daher wird ein weiterer Schritt zurückverfolgt, sodass der gierige Quantifizierer wieder mit einem Zeichen weniger übereinstimmt (wobei das "oo" am Ende der Zeichenfolge nicht übereinstimmt). Das stimmt immer noch nicht mit dem fim regulären Ausdruck überein , daher wird ein weiterer Schritt zurückverfolgt (wobei das "foo" am Ende der Zeichenfolge nicht übereinstimmt ). Jetzt stimmt der Matcher endlich mit dem fim regulären Ausdruck überein ,oosind auch abgestimmt. Erfolg!

Ein widerstrebender oder "nicht gieriger" Quantifizierer stimmt zunächst so wenig wie möglich überein. Das .*stimmt also zunächst nicht überein, so dass die gesamte Zeichenfolge nicht übereinstimmt. Dann versucht der Matcher, fFolgendes zu finden, aber der nicht übereinstimmende Teil der Zeichenfolge beginnt mit "x", sodass dies nicht funktioniert. Der Matcher zieht sich also zurück, sodass der nicht gierige Quantifizierer mit einem weiteren Zeichen übereinstimmt (jetzt stimmt er mit dem "x" überein, sodass "fooxxxxxxfoo" nicht übereinstimmt). Dann versucht es, das zu finden f, was erfolgreich ist, und das ound das nächste oin der Regex-Übereinstimmung auch. Erfolg!

In Ihrem Beispiel wird der Prozess dann mit dem verbleibenden nicht übereinstimmenden Teil der Zeichenfolge "xxxxxxfoo" neu gestartet, wobei derselbe Prozess ausgeführt wird.

Ein besitzergreifender Quantifizierer ist genau wie der gierige Quantifizierer, aber er zieht sich nicht zurück. Es beginnt also damit, .*die gesamte Zeichenfolge abzugleichen und nichts unübertroffen zu lassen. Dann ist nichts mehr übrig, was mit dem fin der Regex übereinstimmt . Da der Possessivquantifizierer nicht zurückverfolgt, schlägt die Übereinstimmung dort fehl.

Anomie
quelle
15
+1 Gute Antwort. Ich würde nur hinzufügen: Lesen Sie Mastering Regular Expressions (3. Ausgabe)
ridgerunner
@ Anomie ein bisschen spät, aber im besitzergreifenden Teil, ich denke du meintest Also beginnt es mit .*+ (beachte das "+")
RD
3
Was genau macht der Possessivquantifizierer dann? wenn es nicht dazu passt? (Ich meine, was ist der Sinn davon, wenn Sie keine Zeichen danach haben können)
Relipse
4
@relipse: Sie würden es in einer Situation verwenden, in der Sie wissen, dass Backtracking nicht hilft, wahrscheinlich nicht mit .*+allem. Wenn Sie beispielsweise ein Muster haben [xyz]*foo, gibt es keine Möglichkeit, dass durch das Zurückverfolgen der mit dem [xyz]*Bit übereinstimmenden x, y und z jemals das folgende fooBit übereinstimmt, sodass Sie die Dinge beschleunigen können, indem Sie es besitzergreifend machen.
Anomie
4
@moodboom, es gibt nie null Fälle (mathematische Tatsache), in denen besitzergreifende Quantifizierer eine Übereinstimmung erzeugen , die nicht von einfachen gierigen Quantifizierern erzeugt wird. Gelegentlich gibt es Fälle , in denen sie produzieren keine Übereinstimmung , wenn gierige Quantifizierer einen produzieren würde Spiel. In ALLEN anderen Fällen (in denen gierig und besitzergreifend die gleichen Ergebnisse erzielt werden) ergeben besitzergreifende Quantifizierer einen Leistungsgewinn.
Wildcard
49

Es ist nur meine Übungsausgabe, um die Szene zu visualisieren.

Visuelles Bild

Islam
quelle
3
Außer ich denke, der letzte Fall, besitzergreifend, sollte keine n Durchgänge haben - greifen Sie einfach die gesamte Saite auf einmal.
Behandle deine Mods gut am
@phyzome Ich denke, es ist jetzt in Ordnung?
Islam
1
Danke für die visuelle Erklärung :)
Lars Moelleken
EXPRESSION .*?fooSollten in () die [f] [o] [o]Rechtecke in nicht gelb sein 5th pass?
Tonix
1
@tonix ja! Die gelbe Färbung muss für den passenden Teil im Ausdruck .*?foound durchgeführt werden .*+foo.
Islam
24

Ich habe die genauen Begriffe "Aufstoßen" oder "Zurückziehen" noch nie gehört. Die Phrase, die diese ersetzen würde, ist "Backtracking", aber "Regurgitate" scheint eine ebenso gute Phrase zu sein wie jede andere für "den Inhalt, der vor dem Backtracking vorläufig akzeptiert wurde, warf ihn wieder weg".

Das Wichtigste, was bei den meisten Regex-Engines zu beachten ist, ist, dass sie zurückverfolgen : Sie akzeptieren vorläufig eine mögliche teilweise Übereinstimmung, während sie versuchen, den gesamten Inhalt des Regex abzugleichen. Wenn der Regex beim ersten Versuch nicht vollständig übereinstimmen kann, wird die Regex-Engine dies tun Rückzieher auf einem seiner Begegnungen. Er wird versuchen , passende *, +, ?, Wechsel oder {n,m}Wiederholung anders, und versuchen Sie es erneut. (Und ja, dieser Vorgang kann lange dauern.)

Das erste Beispiel verwendet den gierigen Quantifizierer. *, Um null oder mehrmals "irgendetwas" zu finden, gefolgt von den Buchstaben "f" "o" "o". Da der Quantifizierer gierig ist, frisst der. * -Teil des Ausdrucks zuerst die gesamte Eingabezeichenfolge. Zu diesem Zeitpunkt kann der Gesamtausdruck nicht erfolgreich sein, da die letzten drei Buchstaben ("f" "o" "o") bereits verbraucht wurden ( von wem? ).

Die letzten drei Buchstaben f, ound owurden bereits von dem ursprünglichen verbrauchten .*Teil der Regel. Das nächste Element in der Regex enthält fjedoch nichts mehr in der Eingabezeichenfolge. Der Motor wird dazu gezwungen , Rückzieher auf seinem ursprünglichen .*Spiel, und versucht , alle-but-die-letzten Zeichen übereinstimmen. (Es mag klug sein und auf alle bis auf die letzten drei zurückgehen, da es drei wörtliche Begriffe enthält, aber mir sind die Implementierungsdetails auf dieser Ebene nicht bekannt.)

Der Matcher zieht sich also langsam zurück ( von rechts nach links? ) Buchstabe für Buchstabe zurück, bis das am weitesten rechts stehende Vorkommen von "foo" wieder aufflammt ( was bedeutet das? )

Dies bedeutet , das foowar vorläufig gewesen auch wenn Anpassung .*. Da dieser Versuch fehlgeschlagen ist, versucht die Regex-Engine, ein Zeichen weniger zu akzeptieren .*. Wenn es ein erfolgreiches Spiel war vor dem.* in diesem Beispiel hätte, würde die Engine wahrscheinlich versuchen, das .*Match zu verkürzen (von rechts nach links, wie Sie betont haben, weil es sich um ein gieriges Qualifikationsspiel handelt), und wenn es nicht passen könnte die gesamten Eingaben, dann könnte es gezwungen sein, neu zu bewerten, was es vor dem .*in meinem hypothetischen Beispiel übereinstimmte .

Zeigen Sie, dass die Übereinstimmung erfolgreich ist und die Suche endet.

Das zweite Beispiel ist jedoch widerstrebend und beginnt mit dem ersten Konsumieren ( von wem? ) "Nichts" zu . Weil "foo"

Das anfängliche Nichts wird von verbraucht .?*, wodurch die kürzest mögliche Menge von allem verbraucht wird , die es dem Rest des regulären Ausdrucks ermöglicht, übereinzustimmen.

erscheint nicht am Anfang der Saite, es ist gezwungen zu schlucken ( wer schluckt?)

Wieder .?*verbraucht der das erste Zeichen, nachdem er den anfänglichen Fehler zurückverfolgt hat, den gesamten regulären Ausdruck nicht mit der kürzestmöglichen Übereinstimmung abzugleichen. (In diesem Fall erweitert die Regex-Engine die Übereinstimmung für .*?von links nach rechts, da dies .*?nur ungern erfolgt.)

erster Buchstabe (ein "x"), der die erste Übereinstimmung bei 0 und 4 auslöst. Unser Testkabelbaum setzt den Prozess fort, bis die Eingabezeichenfolge erschöpft ist. Es findet eine weitere Übereinstimmung bei 4 und 13.

Das dritte Beispiel findet keine Übereinstimmung, weil der Quantifizierer besitzergreifend ist. In diesem Fall wird die gesamte Eingabezeichenfolge von. * + ( Wie? ) Verwendet.

A .*+verbraucht so viel wie möglich und geht nicht zurück , um neue Übereinstimmungen zu finden, wenn der Regex als Ganzes keine Übereinstimmung findet. Da die Possessivform kein Backtracking durchführt, werden Sie wahrscheinlich nicht viele Verwendungen mit .*+, sondern mit Zeichenklassen oder ähnlichen Einschränkungen sehen : account: [[:digit:]]*+ phone: [[:digit:]]*+.

Dies kann den Regex-Abgleich drastisch beschleunigen, da Sie der Regex-Engine mitteilen, dass potenzielle Übereinstimmungen niemals zurückverfolgt werden sollen, wenn eine Eingabe nicht übereinstimmt. (Wenn Sie den gesamten passenden Code von Hand schreiben müssten, wäre dies ähnlich wie wenn Sie ihn niemals verwenden würdenputc(3) ein Eingabezeichen zurückschieben. Es wäre dem naiven Code sehr ähnlich, den man beim ersten Versuch schreiben könnte. Außer Regex-Engines viel besser als ein einzelnes Push-Back-Zeichen, können sie den gesamten Back auf Null zurückspulen und es erneut versuchen. :)

Dies ist jedoch mehr als nur eine mögliche Beschleunigung. Sie können damit auch Regexs schreiben, die genau Ihren Anforderungen entsprechen. Ich habe Probleme, ein einfaches Beispiel zu finden :) Aber das Schreiben eines regulären Ausdrucks mit besitzergreifenden und gierigen Quantifizierern kann zu unterschiedlichen Übereinstimmungen führen, und der eine oder andere ist möglicherweise besser geeignet.

es bleibt nichts übrig, um das "foo" am Ende des Ausdrucks zu befriedigen. Verwenden Sie einen Possessivquantifizierer für Situationen, in denen Sie alles erfassen möchten, ohne sich jemals zurückzuziehen ( was bedeutet Zurücksetzen? ). es wird übertreffen

"Zurücksetzen" bedeutet in diesem Zusammenhang "Zurückverfolgen" - ein vorläufiges Teilmatch wegwerfen, um ein anderes Teilmatch zu versuchen, das möglicherweise erfolgreich ist oder nicht.

der äquivalente gierige Quantifizierer in Fällen, in denen die Übereinstimmung nicht sofort gefunden wird.

Sarnold
quelle
2
Ich vermute, dass es nie einen Fall gibt, in dem ein besitzergreifender Quantifizierer mit etwas übereinstimmt, was ein gieriger Quantifizierer nicht kann. Ich glaube, dass das Folgende es beweist: Ein gieriger Quantifizierer stimmt immer so gut wie möglich überein, und zieht sich dann zurück, wenn er keine Übereinstimmung finden kann. Ein besitzergreifender Quantifizierer stimmt so gut wie möglich überein und wird dann beendet, wenn er keine Übereinstimmung findet. Es kann also etwas geben, mit dem ein gieriger Quantifizierer übereinstimmt, das ein Possessivquantifizierer nicht hat, aber nicht umgekehrt, da beide den "Baum" in derselben Reihenfolge durchsuchen, gibt der Possessivquantifizierer einfach leichter auf. ;)
Wildcard
2
Bestätigt: "Dafür sind Atomgruppierung und Possessivquantifizierer gedacht: Effizienz durch Nicht-Backtracking." from strict-expressions.info Die Aussage in dieser Antwort "Aber mehr als potenzielle Beschleunigungen können Sie damit auch Regexs schreiben, die genau dem entsprechen, was Sie benötigen." ist eigentlich nicht ganz genau.
Wildcard
1
@ Wildcard, danke für die Kommentare; das könnte erklären, warum ich Probleme hatte, ein Beispiel zu finden. Hehe.
Sarnold
19

http://swtch.com/~rsc/regexp/regexp1.html

Ich bin mir nicht sicher, ob dies die beste Erklärung im Internet ist, aber sie ist einigermaßen gut geschrieben und angemessen detailliert, und ich komme immer wieder darauf zurück. Vielleicht möchten Sie es überprüfen.

Wenn Sie eine höhere Ebene (weniger detaillierte Erklärung) für einfache reguläre Ausdrücke wie den von Ihnen betrachteten wünschen, funktioniert eine Engine für reguläre Ausdrücke durch Zurückverfolgen. Im Wesentlichen wählt es einen Abschnitt der Zeichenfolge aus ("isst") und versucht, den regulären Ausdruck mit diesem Abschnitt abzugleichen. Wenn es passt, großartig. Wenn nicht, ändert die Engine die Auswahl des Abschnitts der Zeichenfolge und versucht, den regulären Ausdruck mit diesem Abschnitt abzugleichen, usw., bis alle möglichen Auswahlmöglichkeiten ausprobiert sind.

Dieser Prozess wird rekursiv verwendet: Bei dem Versuch, eine Zeichenfolge mit einem bestimmten regulären Ausdruck abzugleichen, teilt die Engine den regulären Ausdruck in Teile auf und wendet den Algorithmus auf jedes Teil einzeln an.

Der Unterschied zwischen gierigen, widerstrebenden und besitzergreifenden Quantifizierern tritt ein, wenn die Engine auswählt, mit welchem ​​Teil der Zeichenfolge sie übereinstimmen möchten, und wie diese Auswahl geändert werden kann, wenn sie beim ersten Mal nicht funktioniert. Die Regeln lauten wie folgt:

  • Ein gieriger Quantifizierer weist die Engine an, mit der gesamten Zeichenfolge (oder zumindest mit der gesamten Zeichenfolge, die noch nicht mit vorherigen Teilen des regulären Ausdrucks übereinstimmt) zu beginnen und zu überprüfen, ob sie mit dem regulären Ausdruck übereinstimmt. Wenn ja, großartig; Der Motor kann mit dem Rest des regulären Ausdrucks fortfahren. Wenn nicht, wird es erneut versucht, aber ein Zeichen (das letzte) wird aus dem Abschnitt der zu überprüfenden Zeichenfolge entfernt. Wenn das nicht funktioniert, schneidet es ein anderes Zeichen usw. ab. Ein gieriger Quantifizierer überprüft also mögliche Übereinstimmungen in der Reihenfolge vom längsten zum kürzesten.

  • Ein widerstrebender Quantifizierer weist die Engine an, mit dem kürzestmöglichen Teil der Zeichenfolge zu starten. Wenn es übereinstimmt, kann der Motor fortgesetzt werden. Wenn nicht, fügt es dem Abschnitt der zu überprüfenden Zeichenfolge ein Zeichen hinzu und versucht dies usw., bis eine Übereinstimmung gefunden wird oder die gesamte Zeichenfolge aufgebraucht ist. Ein widerstrebender Quantifizierer überprüft also mögliche Übereinstimmungen in der Reihenfolge vom kürzesten zum längsten.

  • Ein Possessiv-Quantifizierer ist wie ein gieriger Quantifizierer beim ersten Versuch: Er weist die Engine an, zunächst die gesamte Zeichenfolge zu überprüfen. Der Unterschied besteht darin, dass der Possessivquantifizierer, wenn es nicht funktioniert, meldet, dass die Übereinstimmung sofort fehlgeschlagen ist. Die Engine ändert den Abschnitt der betrachteten Zeichenfolge nicht und unternimmt keine weiteren Versuche.

Aus diesem Grund schlägt die Possessiv-Quantifizierer-Übereinstimmung in Ihrem Beispiel fehl: Sie .*+wird mit der gesamten Zeichenfolge verglichen, mit der sie übereinstimmt, aber danach sucht die Engine nach zusätzlichen Zeichen foo- aber natürlich findet sie diese nicht, weil Sie Ich bin schon am Ende der Zeichenfolge. Wenn es ein gieriger Quantifizierer wäre, würde er zurückverfolgen und versuchen, die .*einzige Übereinstimmung bis zum vorletzten Zeichen, dann bis zum drittletzten Zeichen und dann bis zum viertletzten Zeichen herzustellen, was erfolgreich ist, weil nur dann dort foolinks, nachdem der .*den früheren Teil der Saite "gegessen" hat.

David Z.
quelle
1
Das ist eine ausgezeichnete Quelle. Ich liebe Zustandsmaschinendiagramme. :)
Regex Rookie
@Regex Rookie: Ich bin froh, dass es Ihnen gefällt :) Nachdem ich mir diese Site angesehen habe, sollte ich klarstellen, dass ihr Zweck darin besteht, eine alternative Implementierung einer Engine für reguläre Ausdrücke zu fördern. Der Backtracking-Algorithmus, den ich (teilweise) und andere Antworten beschreiben, ist der langsame Weg; Es ist ein völlig separater Algorithmus von der auf der Webseite beschriebenen NFA / DFA-Idee. Backtracking ist einfach einfacher zu verstehen, weshalb Regexps normalerweise Anfängern erklärt werden.
David Z
@ David Zaslavsky: Gute Erklärung. Ihre Kommentare in Klammern in "Ein gieriger Quantifizierer weist die Engine an, mit der gesamten Zeichenfolge zu beginnen (oder zumindest mit allem, was noch nicht mit vorherigen Teilen des regulären Ausdrucks übereinstimmt)" sind wichtig. Sie gelten auch für widerstrebende und besitzergreifende Quantifizierer. Dies macht Ihre Erklärung kompatibel mit dem, was passiert, wenn wir unsere Beispielmuster von (". * Foo"; ". *? Foo"; und ". * + Foo") in ("foo. *"; "Foo. *?" Ändern. "; und" foo. * + ").
John Bentley
Tatsächlich stimmt xfooxxxxxxfoo überein. * Foo im normalen (Informatikbedeutung) des regulären Ausdrucks. Die NFA wäre ein Zustand, in dem sie sich mit jedem Charakter untereinander schleift und dann zu foo springen kann. Das EDA wäre eine einfache Übersetzung dieser NFA. Es kann in 8 Staaten durchgeführt werden.
user4951
@ JimThio ja, weil das kein besitzergreifender Quantifizierer ist.
David Z
13

Hier ist meine Einstellung unter Verwendung der Zellen- und Indexpositionen (siehe das Diagramm hier , um eine Zelle von einem Index zu unterscheiden).

Gierig - Passen Sie so viel wie möglich an den gierigen Quantifizierer und den gesamten regulären Ausdruck an. Wenn es keine Übereinstimmung gibt, ziehen Sie den gierigen Quantifizierer zurück.

Eingabezeichenfolge : xfooxxxxxxfoo
Regex : . * Foo

Der obige Regex besteht aus zwei Teilen:
(i) '. *' Und
(ii) 'foo'.

Jeder der folgenden Schritte analysiert die beiden Teile. Zusätzliche Kommentare für eine Übereinstimmung mit "Bestanden" oder "Nicht bestanden" werden in geschweiften Klammern erläutert.

Schritt 1:
(i). * = Xfooxxxxxxfoo - PASS ('. *' Ist ein gieriger Quantifizierer und verwendet die gesamte Eingabezeichenfolge)
(ii) foo = Nach Index 13 - FAIL
Match fehlgeschlagen, kein übereinstimmendes Zeichen mehr .

Schritt 2:
(i). * = Xfooxxxxxxfo - PASS (Backtracking auf dem gierigen Quantifizierer '. *')
(Ii) foo = o - FAIL
Match fehlgeschlagen.

Schritt 3:
(i). * = Xfooxxxxxxf - PASS (Backtracking auf dem gierigen Quantifizierer '. *')
(Ii) foo = oo - FAIL
Match fehlgeschlagen.

Schritt 4:
(i). * = Xfooxxxxxx - PASS (Backtracking auf dem gierigen Quantifizierer '. *')
(Ii) foo = foo - PASS
Report MATCH

Ergebnis: 1 Übereinstimmung (en)
Ich habe den Text "xfooxxxxxxfoo" gefunden, der bei Index 0 beginnt und bei Index 13 endet.

Widerstrebend - Passen Sie so wenig wie möglich an den zögernden Quantifizierer an und passen Sie den gesamten regulären Ausdruck an. Wenn keine Übereinstimmung vorliegt, fügen Sie dem widerstrebenden Quantifizierer Zeichen hinzu.

Eingabezeichenfolge : xfooxxxxxxfoo
Regex: . *? Foo

Der obige reguläre Ausdruck besteht aus zwei Teilen:
(i) '. *?' und
(ii) "foo"

Schritt 1 :
. *? = '' (leer) - PASS (Passen Sie so wenig wie möglich an den widerstrebenden Quantifizierer an. *? '. Index 0 mit' 'ist eine Übereinstimmung.)
foo = xfo - FAIL (Zelle 0,1,2 - dh Index zwischen 0 und 3)
Übereinstimmung fehlgeschlagen.

Schritt 2 :
. *? = x - PASS (Fügen Sie dem widerstrebenden Quantifizierer '. *?' Zeichen hinzu. Zelle 0 mit 'x' ist eine Übereinstimmung.)
foo = foo - PASS
Report MATCH

Schritt 3 :
. *? = '' (leer) - PASS (Passen Sie so wenig wie möglich an den widerstrebenden Quantifizierer an '. *?'. Index 4 mit '' ist eine Übereinstimmung.)
foo = xxx - FAIL (Zelle 4,5,6 - dh Index zwischen 4 und 7)
Übereinstimmung fehlgeschlagen.

Schritt 4 :
. *? = x - PASS (Fügen Sie dem widerstrebenden Quantifizierer '. *?' Zeichen hinzu. Zelle 4.)
foo = xxx - FAIL (Zelle 5,6,7 - dh Index zwischen 5 und 8)
Übereinstimmung fehlgeschlagen.

Schritt 5 :
. *? = xx - PASS (Fügen Sie dem widerstrebenden Quantifizierer '. *?' Zeichen hinzu. Zelle 4 bis 5.)
foo = xxx - FAIL (Zelle 6,7,8 - dh Index zwischen 6 und 9) Übereinstimmung
fehlgeschlagen.

Schritt 6 :
. *? = xxx - PASS (Fügen Sie dem widerstrebenden Quantifizierer '. *?' Zeichen hinzu. Zelle 4 bis 6.)
foo = xxx - FAIL (Zelle 7,8,9 - dh Index zwischen 7 und 10) Übereinstimmung
fehlgeschlagen.

Schritt 7:
. *? = xxxx - PASS (Fügen Sie dem widerstrebenden Quantifizierer '. *?' Zeichen hinzu. Zelle 4 bis 7.)
foo = xxf - FAIL (Zelle 8,9,10 - dh Index zwischen 8 und 11)
fehlgeschlagen.

Schritt 8 :
. *? = xxxxx - PASS (Fügen Sie dem widerstrebenden Quantifizierer '. *?' Zeichen hinzu. Zelle 4 bis 8.)
foo = xfo - FAIL (Zelle 9,10,11 - dh Index zwischen 9 und 12)
fehlgeschlagen.

Schritt 9 :
. *? = xxxxxx - PASS (Fügen Sie dem widerstrebenden Quantifizierer '. *?' Zeichen hinzu. Zelle 4 bis 9.)
foo = foo - PASS (Zelle 10,11,12 - dh Index zwischen 10 und 13)
Report MATCH

Schritt 10 :
. *? = '' (leer) - PASS (Passen Sie so wenig wie möglich an den widerstrebenden Quantifizierer an '. *?'. Index 13 ist leer.
)
Spiel gescheitert.

Ergebnis: 2 Übereinstimmungen
Ich habe den Text "xfoo" gefunden, der bei Index 0
beginnt und bei Index 4 endet. Ich habe den Text "xxxxxxfoo" gefunden, der bei Index 4 beginnt und bei Index 13 endet.

Possessiv - Passen Sie so viel wie möglich an den Possessivquantifer an und passen Sie den gesamten regulären Ausdruck an. NICHT zurückverfolgen.

Eingabezeichenfolge : xfooxxxxxxfoo
Regex: . * + Foo

Die obige Regex besteht aus zwei Teilen: '. * +' Und 'foo'.

Schritt 1 :
. * + = Xfooxxxxxxfoo - PASS (Passen Sie so viel wie möglich an den Possessivquantifizierer '. *' An)
foo = Kein passendes Zeichen mehr - FAIL (Keine Übereinstimmung nach Index 13)
fehlgeschlagen.

Hinweis: Backtracking ist nicht zulässig.

Ergebnis: 0 Übereinstimmung (en)

Raka
quelle
1

Gierig: "Entspricht der längstmöglichen Zeichenfolge"

Widerstrebend: "Entspricht der kürzestmöglichen Zeichenfolge"

Possessiv: Dies ist ein bisschen seltsam, da es NICHT (im Gegensatz zu gierig und widerstrebend) versucht, eine Übereinstimmung für den gesamten regulären Ausdruck zu finden.

Übrigens: Keine Regex Pattern Matcher-Implementierung wird jemals Backtracking verwenden. Alle realen Mustervergleicher sind extrem schnell - nahezu unabhängig von der Komplexität des regulären Ausdrucks!

Tilo Koerbs
quelle
Soweit ich weiß, sind die meisten allgemein verwendeten Implementierungen jetzt so voll mit Funktionen, dass es unmöglich wurde, kein Backtracking zu verwenden. Theoretisch sollten sie in einigen Fällen extrem (exponentiell) langsam sein. In den meisten Fällen sind jedoch spezielle Optimierungen in den Pattern Matcher integriert.
Robert
0

Bei der gierigen Quantifizierung wird während einer Iteration ein Mustervergleich unter Verwendung aller verbleibenden nicht validierten Zeichen einer Zeichenfolge durchgeführt. Nicht validierte Zeichen beginnen in der aktiven Sequenz . Jedes Mal, wenn keine Übereinstimmung auftritt, wird das Zeichen am Ende unter Quarantäne gestellt und die Prüfung erneut durchgeführt.

Wenn nur die führenden Bedingungen des Regex-Musters von der aktiven Sequenz erfüllt werden, wird versucht, die verbleibenden Bedingungen gegen die Quarantäne zu validieren. Wenn diese Validierung erfolgreich ist, werden übereinstimmende Zeichen in der Quarantäne validiert und verbleibende nicht übereinstimmende Zeichen bleiben nicht validiert und werden verwendet, wenn der Prozess in der nächsten Iteration erneut beginnt.

Der Zeichenfluss erfolgt von der aktiven Sequenz in die Quarantäne. Das resultierende Verhalten ist, dass so viel wie möglich von der ursprünglichen Sequenz in einem Match enthalten ist.

Die zögernde Quantifizierung ist meistens dasselbe wie die gierige Qualifizierung, außer dass der Zeichenfluss das Gegenteil ist - das heißt, sie beginnen in der Quarantäne und fließen in die aktive Sequenz . Das resultierende Verhalten ist, dass so wenig wie möglich von der ursprünglichen Sequenz in einem Match enthalten ist.

Possessive Quantification hat keine Quarantäne und enthält alles in einer festen aktiven Sequenz .

Chad Philip Johnson
quelle