Sql-und-Xml - Home

Regular Expressions

Kommentare und Optionen

Kommentare

Kommentare können in RegEx einfach mit (?# dies ist ein Kommentar) an beliebigen Stellen eingefügt werden. Der Kommentar beginnt mit (?# und endet mit der folgenden schließenden Klammer. Dazwischen dürfen beliebige Zeichen, auch '\', '?' und </>, direkt genutzt werden.

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

Optionen

Bei jeder Erstellung eines RegEx-Objektes können zusätzliche Optionen festgelegt werden. Intern werden diese als Bitwerte dargestellt, so daß sie in VB.NET mit 'OR', in C# mit '|' kombiniert werden können. In Perl werden Optionen als Kleinbuchstaben hinter das Suchmuster notiert. In NET können einige Optionen zusätzlich inline festgelegt oder aufgehoben werden und überschreiben den bis dato gültigen Wert. Ferner lassen sich Optionen auch schachteln. So sucht die Zeichenfolge \b(M|A)[a-z] nach einem M oder A am Wortanfang sowie einem folgenden Kleinbuchstaben und findet mit Marmelade am Abend. Sollen zusätzlich alle mit 'm' beginnenden Wörter gefunden werden, so kann man unter Verwendung der IgnoreCase-Option nach \b(?i:M|A)[a-z] suchen. Dies findet von allen vier Wörtern die beiden ersten Buchstaben, ohne als zweiten Buchstaben ebenfalls einen Großbuchstaben zuzulassen. Ist schließlich gewünscht, für das A nur diese einzige Variante zuzulassen, so wird es mit einer weiteren Option geklammert: \b(?i:M|(?-i:A))[a-z] findet mit Marmelade am Abend.

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
mit Marmelade
am Abend
und erhält:

Matchanfang(1)ende(2)

(?-m:(?'anfang'^.)|(?'ende'.$))

mit Marmelade
am Aben
d

mm
d
d

(?+m:(?'anfang'^.)|(?'ende'.$))

mit Marmelade
am Abend

mm
e
e
aa
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: 1
Es definiert das Suchmuster
(?xsn:.+
	# kleiner Kommentar
	\b(.+e)\b
)
wendet dieses auf die Eingabe
Erste Zeile
Zeile zwei
an und liefert als Ergebnis

Erste Zeile
Zeile
 zwei

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).
Diese Option ist bsp. nützlich, falls nur die letzte Fundstelle eines mehrfach auftretenden komplexen Ausdrucks interessiert. Anstatt alle Fundstellen zu ermitteln und sich nun in der Liste der Ergebnisse bis zur letzten Position hindurchzubewegen, sucht man von rechts nach links und erhält das letzte Ergebnis als erstes. Oder es handelt sich um einen Ausdruck, von dem bekannt ist, daß er am Ende der Eingabezeichenfolge zu erwarten ist.

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:

SuchmusterGesamtergebnis(1)(2)
<b>(.+)</b>(.+)u<b>RegEx</b> als <b>Sprache</b> zum SuRegEx als <b>Sprache zum S
<b>(.+?)</b>(.+)u<b>RegEx</b> als <b>Sprache</b> zum SuRegEx als <b>Sprache</b> zum S
<b>(.+)</b>(.+?)u<b>RegEx</b> als <b>Sprache</b> zum SuRegEx</b> als <b>Sprache zum S
<b>(.+?)</b>(.+?)u<b>Sprache</b> zum SuSprache zum S

Ohne RightToLeft, der besseren Übersicht willen hier nochmals dargestellt:

SuchmusterGesamtergebnis(1)(2)
<b>(.+)</b>(.+)u<b>RegEx</b> als <b>Sprache</b> zum SuRegEx</b> als <b>Sprache zum S
<b>(.+?)</b>(.+)u<b>RegEx</b> als <b>Sprache</b> zum SuRegEx als <b>Sprache</b> zum S
<b>(.+)</b>(.+?)u<b>RegEx</b> als <b>Sprache</b> zuRegEx</b> als <b>Sprache z
<b>(.+?)</b>(.+?)u<b>RegEx</b> als <b>Sprache</b> zuRegEx 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 (&#x0069;) die klein geschriebene Version von İ (&#x0130; = 'Latin Capital Letter I with dot above') sowie ı (&#x0131; = '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 If
Diese 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.

© 2003-2016 Jürgen Auer, Berlin.