Gibt es ein definiertes Verhalten dafür, wie reguläre Ausdrücke mit dem Erfassungsverhalten verschachtelter Klammern umgehen sollen? Können Sie insbesondere davon ausgehen, dass verschiedene Engines die äußeren Klammern an der ersten Position und die verschachtelten Klammern an den nachfolgenden Positionen erfassen?
Betrachten Sie den folgenden PHP-Code (unter Verwendung von regulären PCRE-Ausdrücken)
<?php
$test_string = 'I want to test sub patterns';
preg_match('{(I (want) (to) test) sub (patterns)}', $test_string, $matches);
print_r($matches);
?>
Array
(
[0] => I want to test sub patterns //entire pattern
[1] => I want to test //entire outer parenthesis
[2] => want //first inner
[3] => to //second inner
[4] => patterns //next parentheses set
)
Der gesamte Ausdruck in Klammern wird zuerst erfasst (ich möchte testen), und dann werden die inneren Muster in Klammern erfasst ("wollen" und "bis"). Dies ist logisch sinnvoll, aber ich konnte sehen, dass ein ebenso logischer Fall gemacht wurde, bei dem zuerst die Unterklammern und dann das gesamte Muster erfasst wurden.
Ist dieses Verhalten "Capture the Whole Ding First" in Engines für reguläre Ausdrücke definiert oder hängt es vom Kontext des Musters und / oder vom Verhalten der Engine ab (PCRE unterscheidet sich von C # und Java) als etc.)?
Antworten:
Von perlrequick
Vorsichtsmaßnahme : Ohne Klammer zum Öffnen einer nicht erfassten Gruppe (? =)
Aktualisieren
Ich benutze PCRE nicht viel, da ich normalerweise das Original benutze;), aber die Dokumente von PCRE zeigen dasselbe wie die von Perl:
Wenn PCRE von der Perl-Regex-Kompatibilität abweicht, sollte das Akronym möglicherweise neu definiert werden - "Perl Cognate Regular Expressions", "Perl Comparable Regular Expressions" oder so. Oder veräußern Sie einfach die Buchstaben der Bedeutung.
quelle
Ja, das ist alles ziemlich gut definiert für alle Sprachen, die Sie interessieren:
"Erfassungsgruppen werden nummeriert, indem ihre öffnenden Klammern von links nach rechts gezählt werden. ... Gruppe Null steht immer für den gesamten Ausdruck. "
"Erfassungen mit () werden automatisch anhand der Reihenfolge der öffnenden Klammern nummeriert, beginnend mit einer. Die erste erfassen, Element Nummer Null erfassen, ist der Text, der mit dem gesamten Muster des regulären Ausdrucks übereinstimmt. ")
"\ 0 oder $ 0 bezieht sich auf den Text, der mit dem gesamten Muster übereinstimmt. Öffnende Klammern werden von links nach rechts gezählt (beginnend mit 1), um die Nummer des erfassenden Untermusters zu erhalten. " (Dies galt auch für die veralteten POSIX-Funktionen)
PCRE - http://www.pcre.org/pcre.txt
Um Alan M hinzuzufügen, suchen Sie nach "Wie pcre_exec () erfasste Teilzeichenfolgen zurückgibt" und lesen Sie den folgenden fünften Absatz:
$ 1, $ 2 usw. stimmen mit den Erfassungsgruppen überein, wie Sie es erwarten würden (dh durch das Auftreten einer öffnenden Klammer), jedoch gibt $ 0 den Programmnamen zurück, nicht die gesamte Abfragezeichenfolge - um dies zu erreichen, verwenden Sie stattdessen $ &.
Sie werden höchstwahrscheinlich ähnliche Ergebnisse für andere Sprachen (Python, Ruby und andere) finden.
Sie sagen, dass es ebenso logisch ist, zuerst die inneren Erfassungsgruppen aufzulisten, und Sie haben Recht - es ist nur eine Frage der Indizierung beim Schließen und nicht beim Öffnen von Parens. (wenn ich dich richtig verstehe). Dies zu tun ist jedoch weniger natürlich (zum Beispiel folgt es nicht der Konvention zur Leserichtung) und macht es daher schwieriger (wahrscheinlich nicht signifikant), durch Insektion zu bestimmen, welche Erfassungsgruppe bei einem bestimmten Ergebnisindex sein wird.
Es ist auch sinnvoll, die gesamte Übereinstimmungszeichenfolge auf Position 0 zu setzen - hauptsächlich aus Gründen der Konsistenz. Dadurch kann die gesamte übereinstimmende Zeichenfolge unabhängig von der Anzahl der Erfassungsgruppen von Regex zu Regex und unabhängig von der Anzahl der Erfassungsgruppen, die tatsächlich mit etwas übereinstimmen, auf demselben Index bleiben (Java reduziert beispielsweise die Länge des Arrays für übereinstimmende Gruppen für jede Erfassung Die Gruppe stimmt mit keinem Inhalt überein (denken Sie beispielsweise an "ein (. *) Muster"). Sie können jederzeit die Capture_Gruppenergebnisse [Capturing_Gruppenergebnisse_Länge - 2] überprüfen, dies lässt sich jedoch nicht gut in Perl-Sprachen übersetzen, die dynamisch Variablen erstellen ($ 1) , $ 2 usw.) (Perl ist natürlich ein schlechtes Beispiel, da es $ & für den übereinstimmenden Ausdruck verwendet, aber Sie haben die Idee :).
quelle
Jede Regex-Variante, die ich kenne, gruppiert Zahlen in der Reihenfolge, in der die ersten Klammern erscheinen. Dass äußere Gruppen vor ihren enthaltenen Untergruppen nummeriert werden, ist nur ein natürliches Ergebnis, keine explizite Richtlinie.
Wo es interessant wird, ist mit benannten Gruppen . In den meisten Fällen folgen sie der gleichen Politik der Nummerierung nach den relativen Positionen der Eltern - der Name ist lediglich ein Alias für die Nummer. In .NET-Regexen werden die benannten Gruppen jedoch getrennt von nummerierten Gruppen nummeriert. Zum Beispiel:
Regex.Replace(@"one two three four", @"(?<one>\w+) (\w+) (?<three>\w+) (\w+)", @"$1 $2 $3 $4") // result: "two four one three"
Tatsächlich ist die Nummer ein Alias für den Namen . Die den benannten Gruppen zugewiesenen Nummern beginnen dort, wo die "echten" nummerierten Gruppen aufhören. Das mag wie eine bizarre Richtlinie erscheinen, aber es gibt einen guten Grund dafür: In .NET-Regexen können Sie denselben Gruppennamen mehrmals in einer Regex verwenden. Dies ermöglicht Regexe wie die aus diesem Thread zum Abgleichen von Gleitkommazahlen aus verschiedenen Gebietsschemas:
^[+-]?[0-9]{1,3} (?: (?:(?<thousand>\,)[0-9]{3})* (?:(?<decimal>\.)[0-9]{2})? | (?:(?<thousand>\.)[0-9]{3})* (?:(?<decimal>\,)[0-9]{2})? | [0-9]* (?:(?<decimal>[\.\,])[0-9]{2})? )$
Wenn es ein Tausendertrennzeichen gibt, wird es in der Gruppe "Tausend" gespeichert, unabhängig davon, welcher Teil der Regex mit ihm übereinstimmt. Ebenso wird das Dezimaltrennzeichen (falls vorhanden) immer in der Gruppe "Dezimal" gespeichert. Natürlich gibt es Möglichkeiten, die Trennzeichen ohne wiederverwendbare benannte Gruppen zu identifizieren und zu extrahieren, aber diese Methode ist so viel praktischer, dass ich denke, dass sie das seltsame Nummerierungsschema mehr als rechtfertigt.
Und dann gibt es noch Perl 5.10+, mit dem wir mehr Kontrolle über das Erfassen von Gruppen haben, als ich zu tun weiß. : D.
quelle
Die Reihenfolge der Erfassung in der Reihenfolge des linken Parens ist auf allen Plattformen, auf denen ich gearbeitet habe, Standard (Perl, PHP, Ruby, Egrep).
quelle