Offenbar ist diese Konstruktion bei komplexeren Ausdrücken nicht ausreichend. Es gibt deshalb eine zweite Version, welche die Nutzung der Option 'IgnorePatternWhitespace' (x) erfordert. Diese Option wirkt zunächst so, daß die üblichen WhiteSpaces Leerzeichen, Return und Tabulator ignoriert werden. Man beachte, daß mehrere WhiteSpaces nicht auf ein Leerzeichen zusammengezogen werden, wie dies von Html her bekannt ist. Stattdessen werden die WhiteSpaces aus dem pattern entfernt - mit Ausnahme der Leerzeichen in benutzerdefinierten Zeichenklassen ([ ]). Das Suchmuster kann auf mehrere Zeilen aufgeteilt und die Hierarchie der Klammern durch Tabulatoren dargestellt werden. Wird ein Leerzeichen innerhalb eines Suchmusters benötigt, muß es in der Form \ mit Backslash maskiert werden. Ferner ist '#' nutzbar, um einen Kommentar einzuleiten und blendet alles aus, was bis zum Zeilenende folgt. Die beiden folgenden Darstellungen des If-Beispiels sind damit gültig:
Beispiel ohne Kommentare
geehrte (?(r\ ) (?!r\ Herr) | (?!\ Frau) ) (r{0,1}) \s+? (.+?\b)
Beispiel mit Kommentaren zu einzelnen Schritten
geehrte # Suche nach dem Vorspann (?(r\ ) # Test, ob ein r + Leerzeichen folgt (?!r\ Herr) # falls ja, prüfe, ob nicht # 'Herr' folgt | # ansonsten (?!\ Frau) # prüfe, ob nicht 'Frau' folgt ) # Ende If-Klausel (r{0,1}) # zeichne r/Leerstring auf \s+ # nachfolgende Leerzeichen überspringen (.+?\b) # zeichne nächstes Wort auf
Die beiden Optionen Singleline (s) und Multiline (m) schließen sich nicht aus, obwohl dies die Namensgebung nahelegt. Singleline bewirkt nur, daß der Punkt auch den Zeilenwechsel \n findet. .+ liefert jede Zeile der gesamten Eingabezeichenfolge als einzelnes Ergebnis, da der Punkt am Zeilenende das \n nicht akzeptiert. Eine äquivalente Darstellung als Inline-Zuweisung ist (?-s:.+). Die Singleline-Version (?s:.+) findet immer die gesamte Eingabezeichenfolge als ein einziges Ergebnis. Die Option Multiline wirkt sich auf die beiden Zeichen ^ und $ aus. Diese beiden atomaren Assertionen finden ohne Option nur den Anfang und das Ende der gesamten Eingabezeichenfolge. Ist die Option gesetzt, so werden Anfang der Zeichenfolge und jede Position am Anfang einer Zeile sowie das Ende der Zeichenfolge sowie die Position vor jedem Zeilenumbruch gefunden.
Man wähle die Eingabe
und erhält:mit Marmelade
am Abend
Match | anfang(1) | ende(2) |
---|---|---|
(?-m:(?'anfang'^.)|(?'ende'.$)) mit Marmelade am Abend
| ||
m | m | |
d | d | |
(?+m:(?'anfang'^.)|(?'ende'.$)) mit Marmelade am Abend
| ||
m | m | |
e | e | |
a | a | |
d | d |
Das Suchmuster sucht entweder mit ^ den Anfang oder mit $ den Schluß, jeweils ergänzt um einen Punkt, damit das nächstliegende Zeichen im RegEx-Trainer sichtbar wird. Ferner werden beide Zeichen als benannte Teilzeichenfolgen aufgezeichnet. Der gesamte Ausdruck wird einmal mit ausgeschalteter, ein zweites Mal mit eingeschalteter Multiline-Option auf die Eingabe angewandt. Auf das hier eingefügte + zum Aktivieren der Option darf auch verzichtet werden.
Eine spezielle Veränderung gestattet die NET-Option ExplicitCapture (n). Sie erzwingt, daß alle aufzuzeichnenden Teilzeichenfolgen explizit benannt werden müssen. Alle geklammerten Ausdrücke ohne Namen werden damit wie die nicht aufzeichnende Teilzeichenfolge ((?: )) behandelt, so daß umgekehrt deren explizite Kennzeichnung entfallen kann.
Die IgnorePatternWhitespace-Option (x) ermöglicht, wie oben bereits ausgeführt, die Aufteilung eines pattern auf mehrere Zeilen, das Einfügen von WhiteSpace, der ignoriert wird sowie die Verwendung von Kommentaren zwischen '#' und dem Ende der jeweiligen Zeile. Hierin sind sie Kommentaren in Sql ähnlich, bei welchen alles nach '--' bis zum Ende der Zeile ausgeklammert wird. Soll nach einem Leerzeichen gesucht werden, so kann dies entweder in der Form \ oder unter Verwendung des Escapezeichen \s gemacht werden. Ferner ist das Konstrukt [ ] nutzbar, eine benutzerdefinierte Zeichenklasse, die ein Leerzeichen enthält. Aus solchen Klassenkonstrukten entfernt auch die Option IgnorePatternWhitespace niemals Leerzeichen.
Die Option ECMAScript aktiviert ECMA-konformes Verhalten. Damit verhält sich die Suche vergleichbar zur Nutzung von RegEx in JavaScript, das unter dem Begriff 'ECMAScript' standardisiert wurde. Dies entfernt jedoch den Unicode-Bezug weitgehend, so daß Unicode-Block- und Kategorien (\p{}) ausgeschlossen sind. Ferner werden drei vordefinierten Klassen verkleinert: Wortzeichen \w reduzieren sich auf [a-zA-Z_0-9], aus Whitespaces \s wird [ \f\n\r\t\v] und \d stellt nicht mehr alle Zahlzeichen \p{Nd}, sondern nur noch [0-9] dar. Analog werden die Verneinungen dieser Klassen um die ausgeschlossenen Zeichen erweitert. Die Option kann in Fällen wünschenswert sein, in welchen nur Zeichen zulässig sein sollen, die in Urls genutzt werden dürfen. Wird die Option bei der Objekterstellung angegeben, so kann sie höchstens mit den Optionen IgnoreCase, Multiline und Compile verwendet werden. Die drei verbleibenden Inline-Optionen Singleline, ExplicitCapture und IgnorePatternWhitespace sind zwar nicht beim Kompilieren, jedoch inline weiterhin erlaubt und erzeugen die gewünschten Anpassungen. Das folgende VB.NET-Beispiel funktioniert deshalb:
Dim _rE As New RegEx("(?xsn:.+" & Convert.ToChar(10) & _ " # kleiner Kommentar" & Convert.ToChar(10) & _ "\b(.+e)\b" & Convert.ToChar(10) & _ ")", RegExOptions.ECMAScript), _ _str_input As String = "Erste Zeile" & _ Convert.ToChar(10) & "Zeile zwei", _ _mC As MatchCollection _mC = _rE.Matches(_str_input) Console.WriteLine(_mC(0).Value) Console.WriteLine("Zahl der Gruppen: " & _ _mC(0).Groups.Count.ToString()) 'Output: 1Es definiert das Suchmuster
(?xsn:.+ # kleiner Kommentar \b(.+e)\b )wendet dieses auf die Eingabe
Erste Zeile Zeile zweian und liefert als Ergebnis
Erste Zeile
sowie lediglich die Standardgruppe mit der Nummer Null, da der Ausdruck \b(.+e)\b aufgrund der Verwendung der ExplicitCapture-Option (n) keine zweite Gruppe aufzeichnet. Mit der Option ECMAScript läßt sich also auch die SingleLine-Option inline verwenden, so daß der Punkt das Zeilenende Char(10) = \n erkennt.
Die Option RightToLeft bewirkt nur, daß die Eingabezeichenfolge von rechts nach links verarbeitet wird. Das Suchmuster sowie Lookbehind- und Lookahead-Assertionen werden weiterhin von links nach rechts interpretiert. Wird der Punkt auf die Eingabe abcde angewandt, so werden unter Nutzung eines mit RightToLeft deklarierten Objektes die Fundstellen in der Reihenfolge e, d, c, b, a ausgegeben. Eine Suche nach (?'zuvor'.+)c(?'folgend') liefert ab als Wert von 'zuvor' sowie de als Wert für 'folgend'. Das Muster (?=c).. liefert bei ein- und ausgeschalteter Option das Ergebnis cd. Es dreht also die Ausgabe nicht um (dc) und wird selbst auch nicht von rechts nach links interpretiert (cb).
Ein weitergehendes Beispiel für den Einsatz dieser Option besteht in Texten der Form 'A . B ... A B C', falls nur das letzte Tripel 'A B C' gefunden werden soll. Jedes Suchmuster muß die Bausteine A, B und C enthalten, so daß bei einer Suche von links nach rechts zu lange Ketten gefunden werden. Eine Suche in umgekehrter Richtung löst das Problem. Der bereits diskutierte Satz Wir analysieren <b>RegEx</b> als <b>Sprache</b> zum Suchen aus dem Abschnitt über gierige und träge Quantifikatoren liefert ein Beispiel. Die dortige Tabelle sieht, mit der RightToLeft-Option, wie folgt aus:
Suchmuster | Gesamtergebnis | (1) | (2) |
---|---|---|---|
<b>(.+)</b>(.+)u | <b>RegEx</b> als <b>Sprache</b> zum Su | RegEx | als <b>Sprache zum S |
<b>(.+?)</b>(.+)u | <b>RegEx</b> als <b>Sprache</b> zum Su | RegEx | als <b>Sprache</b> zum S |
<b>(.+)</b>(.+?)u | <b>RegEx</b> als <b>Sprache</b> zum Su | RegEx</b> als <b>Sprache | zum S |
<b>(.+?)</b>(.+?)u | <b>Sprache</b> zum Su | Sprache | zum S |
Ohne RightToLeft, der besseren Übersicht willen hier nochmals dargestellt:
Suchmuster | Gesamtergebnis | (1) | (2) |
---|---|---|---|
<b>(.+)</b>(.+)u | <b>RegEx</b> als <b>Sprache</b> zum Su | RegEx</b> als <b>Sprache | zum S |
<b>(.+?)</b>(.+)u | <b>RegEx</b> als <b>Sprache</b> zum Su | RegEx | als <b>Sprache</b> zum S |
<b>(.+)</b>(.+?)u | <b>RegEx</b> als <b>Sprache</b> zu | RegEx</b> als <b>Sprache | z |
<b>(.+?)</b>(.+?)u | <b>RegEx</b> als <b>Sprache</b> zu | RegEx | als <b>Sprache</b> z |
Auffallend ist, daß bei RightToLeft immer als letztes zum Su gefunden wird, das Ergebnis beginnt rückwärts immer mit diesem letzten 'u'. Bei zwei trägen Quantifikatoren wird sogar nur nur ein <b>-Paar gefunden. Bei 'LeftToRight' zwingt das letzte 'u' auch die trägen Quantifikatoren zu einem langen Ergebnis, wohingegen bei 'RightToLeft' nach dem, aus der Rückrichtung her betrachtet, ersten <b> die Suche beendet wird.
Etwas abstrakter formuliert: Hat man Folgen 'A B A B C' eingebettet in größere Texte, so findet eine Suche 'A .+? B .+? C' von links nach rechts maximale, von rechts nach links minimale Teilketten.
Die Option CultureInvariant kann nur bei der Erstellung eines Objektes angegeben werden und wirkt sich aus, falls beim Kompilieren oder inline die Option IgnoreCase (i) genutzt wird. Mit 'IgnoreCase' wechselt das Analysemodul zu einer kulturabhängigen Behandlung beim Vergleich von Unicode-Zeichen. Denn es gibt einige wenige Fälle, in welchen Groß- und Kleinschreibung verschiedenartig zugeordnet sind. So wird in den meisten Kulturen i als die kleingeschriebene Version von I interpretiert. Im Türkischen ist jedoch i (i) die klein geschriebene Version von İ (İ = 'Latin Capital Letter I with dot above') sowie ı (ı = 'Latin Small Letter Dotless I') die klein geschriebene Version von I. Wird nach I ohne Berücksichtigung der Groß/Kleinschreibung gesucht und ist dem aktuellen Thread die türkische Kultur zugeordnet, so darf i nicht gefunden werden, da i dem Zeichen İ als Kleinbuchstabe zugeordnet ist. Die Verwendung der Option 'CultureInvariant' bewirkt, daß diese kulturabhängigen Veränderungen nicht genutzt werden und I auch dann ein i findet, falls dem ausführenden Thread die türkische Kultur zugewiesen wurde.
Man beachte, daß aus einem einzigen solchen Ausnahmefall eine versteckte Sicherheitslücke entstehen kann. Denn es gibt, auch bei serverseitiger Verarbeitung, Fälle, in welchen es wünschenswert ist, die Kultur des die Clientanforderung verarbeitenden Threads durch die Browsereinstellungen des Clients festzulegen, um diesem bsp. Hilfstexte aus Ressourcen in der passenden Landessprache zuzusenden. Darf der Client eigene Sql-Select-Anfragen definieren und werden diese per RegEx geprüft, ob sie kein 'INSERT' enthalten, so kann eine Lücke entstehen. Man betrachte den folgenden Codeausschnitt:
'Nutzer verwendet 'Türkisch' als Browsereinstellung Thread.CurrentThread.CurrentCulture = New CultureInfo("tr-TR") 'Suche nach dem Wort 'INSERT' Dim _rE As New RegEx("(?i:\bINSERT\b)", RegExOptions.CultureInvariant), _ _str_sql_input As String 'Ein Nutzer will den folgenden Sql-Code ausführen _str_sql_input = "insert Into [important-table](user, pwd) ('bob', 'topsecret')" If _rE.Match(_str_sql_input).Success Then Console.Writeline("Unerlaubter Zugriff") Else 'Ausführen des Sql-Befehls in der Annahme, 'daß es sich um einen Select-Befehl handelt End IfDiese Version ist die sichere Variante (abgesehen von 'UPDATE' uvm). Das in lateinischen Großbuchstaben notierte 'INSERT' wird auch dann gefunden, falls der Clientbrowser 'tr-TR' übermittelt und der Sql-Befehl das Wort 'insert' enthält. Wird in diesem Fall jedoch die Option CultureInvariant entfernt, so schlägt die Suche nach 'INSERT' fehl, der Befehl wird ausgeführt. In allen Fällen, in welchen auf der Basis von RegEx-Auswertungen sicherheitsrelevante Entscheidungen getroffen werden, muß entweder die Kultur des ausführenden Thread explizit festgelegt oder die Option 'CultureInvariant' verwendet werden.