Sql-und-Xml - Home

Regular Expressions

Grundlegende Konzepte für die RegEx-Nutzung, Escapezeichen und Klammern mit Sonderbedeutung

Das Prinzip beim Gebrauch von RegEx

Jede Verwendung von RegEx aus dem aktuellen Kontext heraus nutzt zwei Zeichenfolgen als Eingabe - das Suchmuster (pattern) und die zu durchsuchende Zeichenfolge (Eingabezeichenfolge). Ferner läßt sich die Verarbeitung durch Optionen modifizieren. Bei einer Suche wird entweder ein Wahrheitswert (True/False) ausgegeben, falls nur interessiert, ob das Suchmuster gefunden wurde. Es kann jedoch auch entweder die erste Fundstelle oder die Menge aller Fundstellen ausgegeben und von der jeweiligen Programmierumgebung weiterverarbeitet werden. Durch die Verwendung runder Klammern im Suchmuster können Teilausdrücke gesondert benannt und ausgegeben werden. Das Ergebnis erscheint als Wahrheitswert bei der reinen Suche, als Liste von Objekten mit Listen von Unterobjekten, als zweidimensionale Tabelle (RegEx-Trainer) oder als ein zweidimensionales Array. Schließlich können einzelne Ausdrücke nicht nur gesucht, sondern auch ersetzt werden. Die Ersetzung entfernt zwar zunächst die gesamte Fundstelle. Jedoch kann anschließend ein neuer Text eingefügt werden, welcher die Fundstelle ergänzt oder auf einzelnde der aufgezeichneten Teilausdrücke zurückgreift und bsp. deren Reihenfolge vertauscht. So läßt sich eine Datumsangabe von einer deutschen (Tag.Monat.Jahr) in eine amerikanische Form (Monat/Tag/Jahr) umwandeln.

Im Gegensatz zur Suche in Textverarbeitungsprogrammen kann innerhalb von RegEx auch ein positives Ergebnis der Länge Null, also ein Leerstring ausgegeben werden. Dies kann auf zwei verschiedene Arten erzeugt werden. Zum einen gibt es Assertionen / Überprüfungen der Länge Null, die bsp. die Wortgrenzen prüfen. Zum anderen können Quantifikatoren so eingesetzt werden, daß sie null oder mehrere Ergebnisse als Erfolg interpretieren und/oder sich mit möglichst wenig zufriedengeben. In solchen Fällen kann ein Suchmuster, angewandt auf eine Zeichenfolge mit n Zeichen, n Ergebnisse der Länge Null zurückliefern. Im RegEx-Trainer erscheint jedes Ergebnis als neue Zeile, in diesem Fall sind die Zellen leer.

Falls mit C# programmiert wird, so dient der Backslash '\' innerhalb von Texten zur Kennzeichnung von Sonderzeichen. Enthält das Suchmuster in der hiesigen Darstellung einen Backslash, so muß es innerhalb von C# mit verdoppeltem Backslash als '\\' verwendet werden. Wird der Backslash innerhalb von RegEx maskiert, so benötigt C# eine Vierfachmaskierung '\\\\'. Alternativ können in C# Literalzeichenfolgen durch ein vorangestelltes '@' definiert werden, dann entfällt die C#-typische Maskierung (bsp. @"\b", wenn im hiesigen Tutorial "\b" notiert wird). Dies gilt ebenfalls für C++ und JScript, dort gibt es allerdings kein '@'.
Für die Darstellung innerhalb des Tutorials werden zu durchsuchende Zeichenfolgen, Suchmuster und Fundstellen verschiedenartig dargestellt.

Eine typische Verwendung mit VB.NET kann so aussehen:
'Deklaration eines RegEx-Objektes
Dim _rE As New RegEx("und")
'Deklaration eines Objektes für das Ergebnis
Dim _m As Match

'Anwendung auf die Zeichenfolge
_m = _rE.Match("RegEx lernen und nutzen")

'Ausgabe in die Konsole
If _m.Success Then
	Console.WriteLine("Ergebnis: " & _m.Value)
Else
	Console.WriteLine("Nichts gefunden")
End If
Dies liefert die Ausgabe:
Ergebnis: und
Auf den folgenden Seiten wird ein solches Ergebnis in der Form RegEx lernen und nutzen dargestellt.

Zeichen und Escapezeichen

Im einfachsten Fall wird das Suchmuster direkt mit einem Text belegt. Dann steht jedes Zeichen für sich selbst - ein einzelnes a findet jedes 'a' und ignoriert alle anderen Buchstaben. Eine Suche nach reg findet die Stellen RegEx regeneriert mit irregulärer Regelhaftigkeit. Ist die Option IgnoreCase nicht gesetzt, so wird die Groß/Kleinschreibung berücksichtigt, so daß nur zwei Stellen gefunden werden. Wird bei der Erstellung des RegEx-Objektes die Option 'IgnoreCase' festgelegt, so werden Groß/Kleinschreibung ignoriert. Die Deklaration
Dim _rE As New RegEx("reg", RegexOption.IgnoreCase)
findet RegEx regeneriert mit irregulärer Regelhaftigkeit. Dieselbe Option läßt sich inline, innerhalb des Suchmusters festlegen, dies erledigt der Ausdruck (?i:reg).

Diese direkte Einsetzung entspricht dem einfachen Suchen in Textverarbeitungsprogrammen. Sie gilt für alle Zeichen mit Ausnahme der folgenden Elemente:
$ ^ { [ ( | ) * + ? \
Jedes dieser Zeichen gewinnt in einem passenden Kontext eine besondere Bedeutung. Soll ein solches Zeichen direkt, ohne Sonderbedeutung, erkannt werden, so wird es mit '\' maskiert. \? sucht nach dem Fragezeichen, \\ sucht nach dem Backslash. Ferner gibt es Sonderzeichen wie Tabulatur (	) oder einer neuen Zeile (&#A;), für die Escapezeichen (\t bzw. \n) definiert sind. Auf den ersten Blick hiervon nicht unterscheidbar werden auch gewisse Zeichenklassen (\w für Wortzeichen), atomare Assertionen der Breite Null (\b für eine Wortgrenze) und Rückverweisungskonstrukte (\k) in der Form '\Buchstabe' notiert. Diese Vieldeutigkeit des Ausdrucks '\Buchstabe' kann bei der Erarbeitung von RegEx durchaus zur Verwirrung führen, da gleichartig erscheinende Kürzel zu verschiedenen Typen gehören.

Zeichenklassen

Es gibt vordefinierte und benutzerdefinierte Zeichenklassen. Wird im Suchmuster eine Zeichenklasse notiert, so ist die Suche an dieser Stelle erfolgreich, falls ein Zeichen aus dieser Klasse gefunden wird. Vordefinierte Zeichenklassen umfassen Ausdrücke für Unicode-Gruppen oder Blocknamen (\p{Gruppenname|Blockname}), Wortzeichen (\w), Leerraumzeichen (\s) und Dezimalziffern (\d). Das jeweilige Komplement wird durch Großbuchstaben beschrieben. \P{Ll} umfaßt alle Unicode-Zeichen, die nicht lateinische Kleinbuchstaben sind, \W alle Nicht-Wortzeichen, \S alle Nicht-Leerraumzeichen und \D alles, was keine Ziffer ist. Die Unicode-Gruppen werden bislang noch nicht von allen RegEx-Implementierungen unterstützt, .NET und der RegEx-Trainer unterstützen diese Technik. Eine Auflistung der Unicode-Blöcke und Kategorien ist in der lokalen Version der Unicode-Datenbank zu finden.

Ein spezielles Sonderzeichen stellt der Punkt (.) dar. Dieser erfaßt alle Zeichen mit Ausnahme des Zeilenwechsels \n bzw. #xA. Eine quantifizierte Suche mit .+ gibt jede Zeile der Eingabezeichenfolge als eine Fundstelle aus. Die Zahl der Fundstellen ist gleich der Zahl der Zeilen. Dieses Verhalten kann durch die Single-Line-Option (i) modifiziert werden. Diese durchaus mißverständlich interpretierbare Option ändert ausschließlich das Verhalten des Punktes, nun wird auch \n gefunden. Es wird kein Zeichen verändert oder ignoriert, auch wird die Eingabezeichenfolge nicht in einzelne 'single lines' aufgeteilt. Stattdessen wird die gesamte Eingabe als 'eine Zeile' im Sinne von 'einer Einheit' betrachtet. Die Inline-Verwendung der Option mit (?i:.+) findet die gesamte Eingabezeichenfolge als eine einzige Fundstelle.

Benutzerdefinierte Zeichenklassen werden mittels eckiger Klammern ([]) definiert. Die gewünschten Elemente werden aufgezählt. [aeiou] sucht nach einem der fünf Vokale und findet Alle Vokale. Innerhalb eckiger Klammern verlieren alle Sonderzeichen mit Ausnahme von \ und ^ ihre spezielle Bedeutung. \ kann weiterhin dazu benutzt werden, um vordefinierte Zeichenklassen zur durch die Eckklammern deklarierten Zeichenklasse hinzuzufügen. So findet [\p{Sm}\p{Sc}] alle Zeichen der beiden Unicode-Kategorien 'Symbol, Math' sowie 'Symbol, Currency'. Wird als erstes Zeichen innerhalb der eckigen Klammern ein '^' notiert, so definiert dies das Komplement der in den eckigen Klammern stehenden Zeichenmenge. Es werden also alle Zeichen gefunden, die nicht zu dieser Menge gehören. [^aeiou] sucht alle Zeichen, die keine Vokale sind und findet Alle Vokale. Ab der zweiten Position verliert ^ seine Sonderbedeutung und findet sich selbst. Für alle anderen Zeichen ist die Reihenfolge innerhalb der eckigen Klammern irrelevant. Mit einer Zeichenklasse kann deshalb keine spezielle, aus mehreren Zeichen bestehende Zeichenfolge gefunden werden, bei welcher die Reihenfolge der Zeichen zu beachten ist.

Klammern im pattern

Eckige Klammern ([]) werden genutzt, um eine benutzerdefinierte Zeichenklasse zu deklarieren und zu verwenden. An jeden Ausdruck können vordefinierte Quantifikatoren direkt oder selbstdefinierte Quantifikatoren in geschweiften Klammern ({}) folgen. Diese legen fest, wie häufig der hierdurch quantifizierte Ausdruck auftreten muß, damit die Suche erfolgreich ist. Zusätzlich können geschweifte Klammern mit vorausgehendem '\p' oder '\P' dazu genutzt werden, um Unicode-Gruppen oder deren Verneinung zu bezeichnen, \p{Ll} bezeichnet alle Letter-lower-Zeichen, die Kleinbuchstaben.
Ein rundes Klammerpaar deklariert ein Gruppierungskonstrukt. Die durch das Suchmuster in den Klammern gefundene Teilzeichenfolge wird zusätzlich zur gesamten Fundstelle aufgezeichnet und steht bei späteren Auswertungen isoliert zur Verfügung. Jede durch diese Klammern aufgezeichnete übereinstimmende Teilzeichenfolge erhält eine, von 1 an beginnende Nummer, die sich an der Reihenfolge der öffnenden Klammern orientiert. Die gesamte Fundstelle erhält die Nummer 0. Die aufgezeichnete Teilzeichenfolge kann im Suchmuster und bei der Ausführung weiterverwendet werden. Sucht man bsp. in der Zeichenfolge Am 21.06.2050 ist Sommersonnenwende nach den Datumsbestandteilen, so gelingt dies mit \d\d\.\d\d\.\d\d\d\d. Das gesamte Suchmuster liefert jedoch auch den gesamten Datumsausdruck zurück, so daß man Tag, Monat und Jahr zusätzlich extrahieren müßte. Direkt gelingt dies mit dem Ausdruck (\d\d)\.(\d\d)\.(\d\d\d\d). Dann kann (.NET) auf den Tageswert über die Gruppe mit der Nummer 1, auf den Monat über die Gruppe 2 zugegriffen werden. In Perl stehen diese Werte in den Standardvariablen $1 bzw. $2 zur Verfügung.

Eleganter und besser selbstdokumentierend ist eine Lösung mit benannten Teilzeichenfolgen: (?<day>\d\d)\.(?<month>\d\d)\.(?<year>\d\d\d\d). Die in den Spitzklammern definierten Gruppennamen können anschließend direkt verwendet werden, etwa im Rahmen einer Ersetzung: ${month}/${day}/${year} erzeugt 06/21/2050 als Output. Der RegEx-Trainer gibt diese Gruppennamen als Spaltenüberschriften aus. Anstelle der Spitzklammern können auch einfache Hochkommata genutzt werden.

Auf eine im Suchmuster bereits definierte Gruppe kann später durch ein Rückverweis-Konstrukt Bezug genommen werden. Dies gelingt entweder mit der Nummer der Gruppe oder über den Gruppennamen. So kann man zunächst mit (\b\w+\b) ein Wort suchen und findet mit \b(?<doppelt>\w+)\b\s+\k<doppelt> das doppelte das in Wieder ist das das Wetter wechselhaft. Dasselbe gelingt, indem die Nummer der Gruppe in Spitzklammern gesetzt wird, so daß \b(\w+)\b\s+\k<1> dasselbe leistet. Zu beachten ist allerdings, daß atomare Assertionen der Breite Null nicht aufgezeichnet werden. Die Verwendung von \b(?<doppelt>\w+)\b\s+\k<doppelt> findet deshalb auch dies ist unglaublich und undenkbar. Erst der Gebrauch von \b(?<doppelt>\w+)\b\s+\b\k<doppelt>\b schließt solche Fälle aus.

Werden Klammern benötigt, um bsp. Ausdrücke zusammenzufassen und diese zu quantifizieren, ohne daß die Teilzeichenfolge später weiterverarbeitet wird, kann die Aufzeichnung durch die Verwendung einer nichtaufzeichnenden Gruppe verhindert werden: Dies erledigt der Ausdruck (?:   ), die Klammern werden um '?:' ergänzt. Alternativ kann die Option ExplicitCapture (n) genutzt werden. Diese Option erzwingt, daß jede aufzuzeichnende Teilzeichenfolge explizit benannt sein muß. Alle Ausdrücke (  ) ohne Namen werden wie (?:  ) interpretiert.

© 2003-2014 Jürgen Auer, Berlin.