Regex-Rückwärtsreferenzen im Muster: \n und \k<name>
Rückwärtsreferenzen in JavaScript-Regex ermöglichen es, zuvor erfasste Gruppen im Muster selbst zu referenzieren. Hier erfahren Sie alles Wesentliche.
Der größte Teil eines regulären Ausdrucks stimmt mit festem Text überein, aber manchmal muss Text gefunden werden, der identisch mit etwas sein muss, das zuvor erfasst wurde — ohne im Voraus zu wissen, was dieser Text ist. Eine Rückwärtsreferenz löst genau dieses Problem. Sie ermöglicht es einem Muster zu sagen: „Stimme mit dem überein, was die Gruppe gerade eben erfasst hat."
Typische Anwendungsfälle für Rückwärtsreferenzen sind die Erkennung eines doppelten Wortes (the the), die Übereinstimmung einer Zeichenkette in balancierten Anführungszeichen ("..." oder '...', aber nicht "...'), oder die Überprüfung, ob ein HTML-ähnliches Tag durch denselben Tag-Namen geschlossen wird. All das ist mit einfachen Zeichenmustern nicht möglich, da der zu vergleichende Text erst zur Laufzeit des regulären Ausdrucks bekannt ist.
Dieses Handbuch behandelt nummerierte Rückwärtsreferenzen (\1, \2, …), benannte Rückwärtsreferenzen (\k<name>), wie Gruppen nummeriert werden, häufige Fallstricke und wie erfasster Text in String.prototype.replace() wiederverwendet werden kann.
Verwendung von Rückwärtsreferenzen
Innerhalb eines Musters verweist ein Backslash gefolgt von einer Zahl auf den Text, der von einer Erfassungsgruppe erfasst wurde. \1 ist das, was Gruppe 1 gefunden hat, \2 ist Gruppe 2, usw. Der entscheidende Punkt: Es wird der erfasste Text verglichen, nicht das Muster der Gruppe erneut angewendet.
Hier erfasst (\w+) ein Wort in Gruppe 1, \s stimmt mit dem Leerzeichen überein und \1 verlangt dasselbe Wort noch einmal. So stimmt hello hello überein, aber hello world nicht — \1 muss gleich dem sein, was Gruppe 1 erfasst hat, und nicht einfach \w+ ein weiteres Mal.
Wie Gruppen nummeriert werden
Gruppennummern werden durch die Position der öffnenden Klammer jeder Gruppe von links nach rechts vergeben. Dies ist wichtig, wenn mehrere oder verschachtelte Gruppen vorhanden sind:
Der gesamte Treffer ist Gruppe 0 (m[0]), weshalb die erste Erfassungsgruppe \1 und nicht \0 ist. Bei verschachtelten Gruppen erhält die äußere Gruppe die niedrigere Nummer, da ihre ( zuerst kommt.
Verwendung benannter Gruppen
Nummerierte Referenzen werden schwer lesbar, sobald ein Muster größer wird. Stattdessen können Sie einer Gruppe mit (?<name>…) einen Namen geben und mit \k<name> darauf verweisen. Weitere Informationen zur Deklaration benannter Gruppen finden Sie unter Capturing Groups.
Hier ist (?<word>\w+) eine benannte Gruppe und \k<word> referenziert sie als Rückwärtsreferenz. Nach einer erfolgreichen Übereinstimmung ist der erfasste Text auch über das match.groups-object verfügbar. Benannte Gruppen und \k<name> funktionieren in allen modernen Browsern und aktuellen Node.js-Versionen ohne zusätzliche Flags.
Erfasste Inhalte in replace() wiederverwenden
Der häufigste alltägliche Einsatz von Rückwärtsreferenzen ist nicht innerhalb des Musters — sondern im Ersetzungsstring von String.prototype.replace(). Dort wird erfasster Text mit $1, $2, … (oder $<name> für benannte Gruppen) referenziert.
Ein nützliches Beispiel fasst ein versehentlich doppelt vorhandenes Wort auf eines zusammen:
Beachten Sie den Unterschied: \1 (Backslash) wird innerhalb des Musters verwendet, während $1 (Dollarzeichen) im Ersetzungsstring eingesetzt wird. Beide zu verwechseln ist eine häufige Fehlerquelle.
Fallstrick: Nicht beteiligte Gruppen
Eine Rückwärtsreferenz auf eine Gruppe, die nicht an der Übereinstimmung beteiligt war, verhält sich besonders. Wenn die Gruppe nie übereinstimmte (zum Beispiel weil sie in einer nicht verwendeten Alternative war), ist ihr erfasster Wert undefined, und in JavaScript stimmt die Rückwärtsreferenz dann mit der leeren Zeichenkette überein — sie gelingt, ohne etwas zu verbrauchen.
Dies ist leicht zu übersehen: Man könnte erwarten, dass \1 fehlschlägt, wenn die Gruppe nicht übereinstimmte, aber stattdessen stimmt sie stillschweigend mit nichts überein. Gestalten Sie Ihre Alternativen sorgfältig, wenn Sie darauf angewiesen sind, dass eine Gruppe immer etwas erfasst hat.
Ein klassischer Anwendungsfall: Balancierte Anführungszeichen
Ein praktisches Muster, das zwingend eine Rückwärtsreferenz benötigt, ist die Übereinstimmung einer Zeichenkette in Anführungszeichen, bei der das schließende Anführungszeichen dasselbe Zeichen wie das öffnende sein muss — "..." und '...' sind gültig, aber "...' nicht.
Die Gruppe (['"]) erfasst das öffnende Anführungszeichen, und \1 erzwingt, dass das schließende Zeichen exakt dasselbe ist. Ein einfaches ["'].*?["'] könnte dies nicht durchsetzen — es würde fröhlich "...' akzeptieren. Das ist der Unterschied zwischen einem Lookahead/Lookbehind (der nur prüft) und einer Rückwärtsreferenz (die den erfassten Text erneut abgleicht).
Zusammenfassung
Verwenden Sie eine Rückwärtsreferenz, wenn ein späterer Teil der Übereinstimmung gleich dem zuvor gefundenen Text sein muss — doppelte Wörter, balancierte Anführungszeichen, gleichnamige Tags oder Regeln wie „benachbarte Zeichen müssen sich unterscheiden". Behalten Sie die drei wesentlichen Punkte im Gedächtnis:
- Nummerierte Gruppen werden durch ihre öffnende
(gezählt, beginnend bei\1; der gesamte Treffer ist Gruppe 0. - Verwenden Sie
\1/\k<name>innerhalb des Musters und$1/$<name>inreplace(). - Eine nicht beteiligte Gruppe lässt ihre Rückwärtsreferenz mit der leeren Zeichenkette übereinstimmen — strukturieren Sie Ihre Alternativen daher sorgfältig.
Für die Grundbausteine lesen Sie Capturing Groups, Character Classes und Lookahead and Lookbehind.