Verschachtelte Klammerpaare
(Auszug aus "Reguläre Ausdrücke" von Jeffrey E. F. Friedl)
Die Mustersuche nach paarweise vorkommenden Elementen wie Klammern (runden, spitzen, eckigen, geschweiften) usw. ist ein häufiges Problem. Es stellt sich meist, wenn Konfigurationsdateien oder Programme verarbeitet werden sollen. Nehmen wir an, wir wollten die Argumentenliste einer Funktion aus einem C-Programm herausholen. Argumente folgen dem Funktionsnamen und sind eingeklammert, die Argumente selbst können aber wieder Klammern enthalten, entweder weil sie selbst Funktionsaufrufe sind oder weil Klammern bei algebraischen Ausdrücken auftreten. Zunächst würde man, unter Missachtung von verschachtelten Klammern, etwa Folgendes versuchen: ˹\bfoo\([^)]*\)˼, aber das funktioniert nicht.
In guter C-Tradition heißt die Funktion im Beispiel natürlich foo. Der hervorgehobene Teil der Regex ist der, der auf die Argumente der Funktion passen soll. Mit einem Suchtext wie foo(2,●.0) und foo(somevar,●3.7) funktioniert das wie gewünscht. Leider findet die Regex auch foo(bar(somevar),●3.7), also nicht das Gewünschte. Wir brauchen etwas »Schlaueres« als ˹[^)]*˼; denkbar wären etwa:
Regex | Beschreibung | |
---|---|---|
1. | \(.*\) | Literale Klammern mit irgendetwas dazwischen |
2. | \([^)]*\) | Von einer öffnenden Klammer bis zur nächsten schließenden Klammer |
3. | \([^()]*\) | Von einer öffnenden Klammer bis zur nächsten schließenden Klammer, ohne irgendwelche Klammern dazwischen |
Die folgende Abbildung zeigt, was diese Ausdrücke bei einem Beispieltext finden würden.
Abbildung: Was unsere Beispielausdrücke finden würden.
Wie Sie sehen, erkennt Regex 1 zu viel Text (Anmerkung: Bei ˹.*˼ sollte bei Ihnen ein Warnlicht aufleuchten: Vorsicht, ist der Punkt wirklich das, worauf Sie den Stern anwenden wollen? Manchmal stimmt das ja, aber häufig wird ˹.*˼ falsch benutzt.) und Regex 2 zu wenig. Regex 3 passt überhaupt nicht – isoliert würde ›(dies)‹ zwar erkannt, aber die öffnende Klammer muss unmittelbar auf den Funktionsnamen foo folgen. Also erfüllt keiner der drei Ausdrücke den Zweck.
Das hat seinen Grund: Mit regulären Ausdrücken können beliebig verschachtelte Klammerausdrücke nicht erkannt werden. Es geht einfach nicht. Das war bis vor Kurzem ein universell gültiger Satz. Neuerdings gibt es jedoch in Perl, in den .NET-Sprachen und in PCRE/PHP Konstrukte, die genau dies dennoch ermöglichen (siehe dazu Verschachtelte Paare mit dynamischen regulären Ausdrücken erkennen, Verschachtelte Konstrukte erkennen und Text mit verschachtelten Klammern parsen).
Auch ohne diese Erweiterungen kann man immerhin einen regulären Ausdruck konstruieren, der bis zu einer bestimmten Verschachtelungstiefe funktioniert, aber nicht für beliebig tiefe Verschachtelungen. Ein regulärer Ausdruck, der nur gerade eine Verschachtelung zulässt, ist zum Beispiel:
˹\([^()]*(\([^()]*\)[^()]*)*\)˼
Der Gedanke, tiefere Verschachtelungen zuzulassen, wird damit schon beängstigend. Und doch – das folgende kleine Perl-Programmstück konstruiert zu einer Verschachtelungstiefe $depth die entsprechende Regex. Es verwendet dazu den »String x Anzahl«-Operator, der einen String konstruiert, der aus Anzahl zusammengesetzten Strings besteht.
$regex = '\(' . '(?:[^()]|\(' x $depth . '[^()]*' . '\))*' x $depth . '\)';
Tipp der data2type-Redaktion: Zum Thema Reguläre Ausdrücke bieten wir auch folgende Schulungen zur Vertiefung und professionellen Fortbildung an: |
Copyright der deutschen Ausgabe © 2008 by O’Reilly Verlag GmbH & Co. KG
Für Ihren privaten Gebrauch dürfen Sie die Online-Version ausdrucken.
Ansonsten unterliegt dieses Kapitel aus dem Buch "Reguläre Ausdrücke" denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.
O’Reilly Verlag GmbH & Co. KG, Balthasarstr. 81, 50670 Köln