Ich habe gerade eine Frage gelesen, wie man Daten in doppelte geschweifte Klammern bekommt ( diese Frage ), und dann hat jemand Ausgleichsgruppen angesprochen. Ich bin mir immer noch nicht ganz sicher, was sie sind und wie man sie benutzt.
Ich habe die Definition der Ausgleichsgruppe durchgelesen , aber die Erklärung ist schwer zu befolgen, und ich bin immer noch ziemlich verwirrt über die Fragen, die ich erwähnt habe.
Könnte jemand einfach erklären, was Ausgleichsgruppen sind und wie sie nützlich sind?
c#
.net
regex
balancing-groups
Es ist nicht wahr.
quelle
quelle
Antworten:
Soweit ich weiß, sind Ausgleichsgruppen für die Regex-Variante von .NET einzigartig.
Nebenbei: Wiederholte Gruppen
Zunächst müssen Sie wissen, dass .NET (soweit ich weiß) die einzige Regex-Variante ist, mit der Sie auf mehrere Erfassungen einer einzelnen Erfassungsgruppe zugreifen können (nicht in Rückreferenzen, sondern nach Abschluss des Spiels).
Um dies anhand eines Beispiels zu veranschaulichen, betrachten Sie das Muster
und die Zeichenfolge
"abcd"
.Bei allen anderen Regex-Varianten führt die Erfassungsgruppe
1
lediglich zu einem Ergebnis:d
(Beachten Sie, dass die vollständige Übereinstimmung natürlichabcd
wie erwartet erfolgt.) Dies liegt daran, dass bei jeder neuen Verwendung der Erfassungsgruppe die vorherige Erfassung überschrieben wird..NET hingegen erinnert sich an alle. Und das in einem Stapel. Nach dem Abgleichen der obigen Regex wie
Sie werden das finden
Ist ein,
CaptureCollection
dessen Elemente den vier Erfassungen entsprechenDabei ist die Nummer der Index in der
CaptureCollection
. Grundsätzlich wird jedes Mal, wenn die Gruppe erneut verwendet wird, eine neue Erfassung auf den Stapel verschoben.Interessanter wird es, wenn wir benannte Erfassungsgruppen verwenden. Da .NET die wiederholte Verwendung des gleichen Namens ermöglicht, können wir einen regulären Ausdruck wie schreiben
um zwei Wörter in derselben Gruppe zu erfassen. Jedes Mal, wenn eine Gruppe mit einem bestimmten Namen angetroffen wird, wird eine Erfassung auf ihren Stapel verschoben. Wenden Sie diesen regulären Ausdruck also auf die Eingabe an
"foo bar"
und überprüfen Sie ihnWir finden zwei Aufnahmen
Auf diese Weise können wir sogar Dinge aus verschiedenen Teilen des Ausdrucks auf einen einzelnen Stapel verschieben. Dies ist jedoch nur die Funktion von .NET, mit der mehrere in dieser Liste aufgeführte Aufnahmen verfolgt werden können
CaptureCollection
. Aber ich sagte, diese Sammlung ist ein Stapel . So können wir knallen Dinge von ihm?Geben Sie ein: Gruppen ausgleichen
Es stellt sich heraus, dass wir es können. Wenn wir eine Gruppe wie verwenden
(?<-word>...)
, wird die letzte Erfassung vom Stapel entfernt,word
wenn der Unterausdruck...
übereinstimmt. Wenn wir also unseren vorherigen Ausdruck in ändernDann wird die zweite Gruppe die Aufnahme der ersten Gruppe platzen lassen und wir werden
CaptureCollection
am Ende eine leere erhalten . Natürlich ist dieses Beispiel ziemlich nutzlos.Die Minus-Syntax enthält jedoch noch ein weiteres Detail: Wenn der Stapel bereits leer ist, schlägt die Gruppe fehl (unabhängig von ihrem Untermuster). Wir können dieses Verhalten nutzen, um Verschachtelungsebenen zu zählen - und hier kommt die Namensausgleichsgruppe her (und dort wird es interessant). Angenommen, wir möchten Zeichenfolgen abgleichen, die korrekt in Klammern stehen. Wir schieben jede öffnende Klammer auf den Stapel und fügen für jede schließende Klammer eine Aufnahme hinzu. Wenn eine schließende Klammer zu viele enthält, wird versucht, einen leeren Stapel zu platzieren, und das Muster schlägt fehl:
Wir haben also drei Alternativen in einer Wiederholung. Die erste Alternative verbraucht alles, was keine Klammer ist. Die zweite Alternative entspricht
(
s, während sie auf den Stapel geschoben werden. Die dritte Alternative stimmt mit)
s überein, während Elemente vom Stapel entfernt werden (falls möglich!).Hinweis: Zur Verdeutlichung überprüfen wir nur, dass keine nicht übereinstimmenden Klammern vorhanden sind! Dies bedeutet, dass Zeichenfolgen, die überhaupt keine Klammern enthalten , übereinstimmen, da sie syntaktisch noch gültig sind (in einer Syntax, in der Ihre Klammern übereinstimmen müssen). Wenn Sie mindestens einen Satz Klammern sicherstellen möchten, fügen Sie einfach
(?=.*[(])
direkt nach dem einen Lookahead hinzu^
.Dieses Muster ist jedoch nicht perfekt (oder völlig korrekt).
Finale: Bedingte Muster
Es gibt noch einen Haken: Dies stellt nicht sicher, dass der Stapel am Ende der Zeichenfolge leer ist (daher
(foo(bar)
gültig). .NET (und viele andere Varianten) haben ein weiteres Konstrukt, das uns hier hilft: bedingte Muster. Die allgemeine Syntax lautetwo das
falsePattern
optional ist - wenn es weggelassen wird, stimmt der falsche Fall immer überein. Die Bedingung kann entweder ein Muster oder der Name einer Erfassungsgruppe sein. Ich werde mich hier auf den letzteren Fall konzentrieren. Wenn es sich um den Namen einer Erfassungsgruppe handelt,truePattern
wird dieser nur dann verwendet, wenn der Erfassungsstapel für diese bestimmte Gruppe nicht leer ist. Das heißt, ein bedingtes Muster wie(?(name)yes|no)
"Wennname
etwas übereinstimmt und erfasst wurde (das sich noch auf dem Stapel befindet), verwenden Sie ein Muster,yes
andernfalls verwenden Sie ein Musterno
".Am Ende unseres obigen Musters könnten wir also so etwas hinzufügen,
(?(Open)failPattern)
was dazu führt, dass das gesamte Muster fehlschlägt, wenn derOpen
-stack nicht leer ist. Das Einfachste, um das Muster bedingungslos zum Scheitern zu bringen, ist(?!)
(ein leerer negativer Lookahead). Wir haben also unser letztes Muster:Beachten Sie, dass diese bedingte Syntax per se nichts mit dem Ausgleich von Gruppen zu tun hat, sondern dass die volle Leistung genutzt werden muss.
Von hier aus ist der Himmel die Grenze. In Kombination mit anderen .NET-Regex-Funktionen wie Lookbehinds mit variabler Länge ( die ich selbst auf die harte Tour lernen musste) sind viele sehr anspruchsvolle Anwendungen möglich, und es gibt einige Fallstricke . Die Hauptfrage ist jedoch immer: Ist Ihr Code bei Verwendung dieser Funktionen noch wartbar? Sie müssen es wirklich gut dokumentieren und sicherstellen, dass jeder, der daran arbeitet, diese Funktionen auch kennt. Andernfalls ist es möglicherweise besser, wenn Sie die Zeichenfolge nur manuell Zeichen für Zeichen durchlaufen und die Verschachtelungsebenen in einer Ganzzahl zählen.
Nachtrag: Was ist mit der
(?<A-B>...)
Syntax?Credits für diesen Teil gehen an Kobi (siehe seine Antwort unten für weitere Details).
Mit all dem können wir nun überprüfen, ob eine Zeichenfolge korrekt in Klammern steht. Aber es wäre viel nützlicher, wenn wir tatsächlich (verschachtelte) Captures für alle Inhalte dieser Klammern erhalten könnten. Natürlich können wir uns daran erinnern, Klammern in einem separaten Erfassungsstapel geöffnet und geschlossen zu haben, der nicht geleert wird, und dann in einem separaten Schritt eine Teilzeichenfolgenextraktion basierend auf ihren Positionen durchführen.
Aber .NET bietet hier noch eine weitere Komfortfunktion: Wenn wir verwenden
(?<A-B>subPattern)
, wird nicht nur ein Capture vom Stapel genommenB
, sondern auch alles zwischen diesem Popup vonB
und dieser aktuellen Gruppe wird auf den Stapel verschobenA
. Wenn wir also eine solche Gruppe für die schließenden Klammern verwenden, während wir Verschachtelungsebenen von unserem Stapel entfernen, können wir den Inhalt des Paares auch auf einen anderen Stapel verschieben:Kobi hat diese Live-Demo in seiner Antwort bereitgestellt
Wenn wir all diese Dinge zusammen nehmen, können wir:
Alles in einem einzigen regulären Ausdruck. Wenn das nicht aufregend ist ...;)
Einige Ressourcen, die ich hilfreich fand, als ich zum ersten Mal davon erfuhr:
quelle
Nur eine kleine Ergänzung zu M. Buettners hervorragender Antwort:
Was ist mit der
(?<A-B>)
Syntax los?(?<A-B>x)
unterscheidet sich subtil von(?<-A>(?<B>x))
. Sie führen zum gleichen Kontrollfluss * , erfassen jedoch unterschiedlich.Schauen wir uns zum Beispiel ein Muster für ausgewogene Zahnspangen an:
Am Ende des Spiels haben wir eine ausgeglichene Saite, aber das ist alles, was wir haben - wir wissen nicht, wo sich die Klammern befinden, weil der
B
Stapel leer ist. Die harte Arbeit, die der Motor für uns geleistet hat, ist weg.( Beispiel zu Regex Storm )
(?<A-B>x)
ist die Lösung für dieses Problem. Wie? Es wird nicht erfasstx
in$A
: Es erfasst den Inhalt zwischen der vorherigen ErfassungB
und der aktuellen Position.Verwenden wir es in unserem Muster:
Dies würde
$Content
für jedes Paar auf dem Weg in die Saiten zwischen den Klammern (und ihren Positionen) erfassen .Für die Saite
{1 2 {3} {4 5 {6}} 7}
würde es vier Aufnahmen sein:3
,6
,4 5 {6}
, und1 2 {3} {4 5 {6}} 7
- viel besser als nichts oder}
}
}
}
.( Beispiel - Klicken Sie auf die
table
Registerkarte und schauen Sie sich an${Content}
, erfasst )Tatsächlich kann es ohne Ausgleich verwendet werden:
(?<A>).(.(?<Content-A>).)
Erfasst die ersten beiden Zeichen, obwohl sie durch Gruppen getrennt sind.(Ein Lookahead wird hier häufiger verwendet, skaliert jedoch nicht immer: Es kann Ihre Logik duplizieren.)
(?<A-B>)
ist eine starke Funktion - sie gibt Ihnen die genaue Kontrolle über Ihre Aufnahmen. Denken Sie daran, wenn Sie versuchen, mehr aus Ihrem Muster herauszuholen.quelle
|'[^']*'
der richtigen Stelle hinzufügen : Beispiel . Wenn Sie auch maskierte Zeichen benötigen, finden Sie hier ein Beispiel: (Regex für übereinstimmende C # -String- Literale) [ stackoverflow.com/a/4953878/7586] .