3. Teil
So,
das war ja einfach. Allerdings auch wenig interessant. Wenn sich die
Möglichkeiten von Regexen in den einfachen Suchmustern erschöpften, wäre
es wohl kaum wert, sie in einem Tutorial vorzustellen.
Es muss also mehr geben! Fangen wir mal an:
3.1. Zeilengrenzen
Außer
einfach den Text irgendwo zu suchen, kann man ein Regex veranlassen, an
bestimmten Stellen zu suchen. Dafür gibt es Metazeichen: ^ $ (Ja, liebe
Experten, lasst uns für den Anfang die Zeichenkette als eine Zeile
betrachten, ok?)
Konkret: „^dies oder das“. Die Zeichenfolge wird nur gefunden, wenn das
Wort „dies“ unmittelbar am Anfang einer Zeile steht und von “ oder das“
gefolgt wird.
Oder: „Das ist das Ende$“ wird genau nur am Zeilenende gefunden. Es ist
egal, was vor der Zeichenfolge erscheint, aber „Das ist das Ende“ muss
mit der Zeile enden.
Diese beiden Metazeichen sollten unter anderem eingesetzt werden, um das
Regex zu beschleunigen. Warum wird das Regex schneller? Nehmen wir mal
wieder unser Beispiel „^dies oder das“ und wenden das auf den Text ‚Es
war einmal‘ an: die Regex-Maschine prüft zunächst, ob es sich bei dem
ersten Zeichen um den Zeilenanfang handelt: das liefert WAHR zurück.
Dann prüft die Maschine das nächste Zeichen darauf, ob es ein „d“ ist,
was natürlich fehlschlägt. Die Suche wird sofort an dieser Stelle mit
dem Ergebnis FALSCH abgebrochen. Ohne das ^-Zeichen hätte die Maschine
das zweite, dritte, vierte usw. Zeichen geprüft und versucht „dies oder
das“ im String zu finden, um am Ende festzustellen, dass die
Zeichenfolge nicht existiert.
3.2. Wortgrenzen
Neben
den Zeilengrenzen sind Wortgrenzen interessant, allerdings selten
beachtet. Mit „\b“ wird das Regex veranlasst, den betreffenden String
mit einer Wortgrenze zu suchen: „\bdies oder das“
Kennen wir schon, nicht wahr? Das Beispiel vom Anfang, in dem diese
beiden Wörter in ‚Paradies oder das Weib‘ gefunden wurde, würde nun
nicht mehr gefunden. Ich erinnere mich an eine Diskussion in einer
Mailingliste vor längerer Zeit, in der gefragt wurde, was der Sinn des
\b sein solle, da man doch auch die Wortgrenze über ein Leerzeichen
ermitteln könne. Das ist nur bedingt richtig, denn Wörter enden auch an
Interpunktionszeichen: „er “ findet in der Tat ‚der Regen‘, aber
‚wieder.‘ als Satzabschluss würde nicht gefunden. Dies lässt sich mit
„er\b“ umgehen.
Natürlich ist das auch wieder negierbar: „\B“ bedeutet, dass diese Zeichenkette nicht an Wortgrenzen gefunden werden soll.
Auch hierzu ein Beispiel: „Re\B.“ Also, es soll die Zeichenkette ‚Re‘,
aber nur, wenn sie nicht die Wortgrenze darstellt, aber von einem
beliebigen Zeichen gefolgt wird. Wenden wir das mal auf ‚Re: oder
Reply:‘ an. Der Test im Tester ergibt ‚Rep‘. Tauscht mal \B gegen \b aus
und es wird ‚Re:‘ gefunden. Alles klar?
3.3. Alternativen
Wir
erinnern uns an das erste Regex „dies oder das“ in diesem Kurs? Dazu
schrieb ich den eigentlich überflüssigen Zusatz, dass dies nicht
bedeutet, das Regex würde entweder nach „dies“ oder nach „das“ suchen.
Na ja, so überflüssig war das nicht: ich wollte doch schließlich
irgendwas haben, was eine Einleitung für diesen Abschnitt bietet *g*.
Genau um diese Alternativen, nämlich dem ODER, dreht es sich nun.
Um nach alternativen Zeichenketten zu suchen, bieten die Regexe ein
besonderes Zeichen: „|“ den Strich, den einige von euch auch als
Pipe-Symbol kennen. Konkretes Beispiel: „dies|das“ Das Regex prüft, ob
es die Zeichenkette ‚dies‘ findet. Wenn es diese nicht findet, sucht es
nach der zweiten Alternative.
Was passiert aber, wenn beide Alternativen im Text enthalten sind? Nun,
man sollte glauben, dass die Alternative gefunden wird, die als erste im
Regex steht. Dem ist aber nicht so! Vielmehr wird als gefundener Text
das erste Auftreten einer der Alternativen in der Zeichenkette
zurückgegeben!
Beispiel:
Gegeben sei das Regex „das|dies|jenes“ und die Zeichenkette ‚Es war
jenes Auto, das wir diesseits der Böschung geborgen haben‘ (Die
Beispielzeichenketten sind nicht immer hochliterarisch, aber sie sollten
dennoch helfen ;-)) Als gefundener Text wird das ‚jenes‘
zurückgeliefert. Probiert es mal im Regex-Tester aus. Diese Besonderheit
wird noch mal für uns interessant, wenn wir zu dem Abschnitt kommen, in
welchem wir uns Textstücke mit dem Regex merken wollen.
Solche Alternativen sind auch kombinierbar: „^aw:|^wg:|^fwd:“ bedeutet,
das nach diesen drei Möglichkeiten alternativ gesucht wird. In allen
drei Fällen wird nach dem Zeilenanfang gesucht und hinter jeder
Zeichenkombination steht ein Doppelpunkt . Ihr habt recht: das müsste
man vereinfachen können. Und genau wie in der Mathematik kann ich hier
Klammern setzen und das Regex vereinfachen und beschleunigen:
„^(aw|wg|fwd):“
Derartige Vereinfachungen müssen nicht immer einfacher zu lesen sein:
„d(ie|a)s“ wäre die Vereinfachung zu unserem Eingangsbeispiel in diesem
Abschnitt, was zwar richtig wäre, aber nicht gerade den Lesefluß
unterstützt 😉
3.4. Besondere Klassen
Einige
der Zeichenklassen haben wir schon oben kennen gelernt. Ich möchte hier
noch ein paar von unterschiedlicher Bedeutung nachschieben.
In fast jedem Regex werdet ihr die Zeichenklasse „\s“ finden. Sie steht
für so genannte Whitespace-Zeichen, also alle Zeichen, die einen weißen
Raum auf den Bildschirm zeigen: Leerzeichen, Tabulatoren, die Befehle
‚Neue Zeile‘, ‚Wagenrücklauf‘, ‚Blattvorschub‘. Es wird genügen, sich zu
merken, dass jede leere Stelle in einer Zeichenkette hiervon gefunden
wird. Natürlich kann man dies auch negieren: „\S“ passt auf jedes andere
Zeichen, das eben nicht weißen Platz beansprucht.
„\A“ wird seltener eingesetzt: es passt auf den Anfang einer
Zeichenkette. Dies ist nicht der Beginn einer Zeile, denn das hätten wir
mit „^“ gesucht. Analog hierzu gibt es „\Z“: dieses findet das Ende der
Zeichenkette oder anders gesagt: es findet das Zeichen unmittelbar vor
dem ‚Neue Zeile‘-Befehl. Und erneut: dies ist nicht das Ende einer
Zeile, denn das hätten wir mit „$“ gesucht. Wir werden den Unterschied
später sehen, wenn wir zu den Optionen kommen. Sorry, aber ihr müsst
euch etwas gedulden *g*.
3.5. Übersicht über dieses Kapitel
Dieser Abschnitt hat uns ein paar neue Möglichkeiten zur Suchmusterdefinition gezeigt:
- Zeilengrenzen haben eigene Suchkriterien: ^ für den Zeilenanfang und $ für das Zeilenende.
- Für Wortgrenzen gibt es „\b“ mit dem nach Zeichenketten gesucht werden kann, die den Abschluss eines Wortes bilden. „\B“ steht für Zeichenketten, die nicht Wortgrenzen darstellen.
- Man kann Zeichenketten als Alternativen definieren. Dazu trennt man jede Alternative mit dem Strich „|“ ab. Gleiche Zeichen an gleichen Stellen innerhalb der Alternativen, lassen sich wie in der Mathematik vor oder hinter eine Klammer ziehen: „^(Re|Aw|Fwd)“ Für alle drei Alternativen gilt, dass sie am Anfang einer Zeile stehen müssen.
- Leerzeichen oder Tabulatoren werden als so genannte Whitespace-Character bezeichnet, für die ein spezielles Suchmuster existiert: \s Negiert schreibt man für diese Gruppe \S
- Auch Anfang und Ende einer Zeichenkette sind suchbar: \A und \Z
Aufgaben:
1. Gegeben: „(R.:$|^R.:)“ sowie die Zeichenkette ‚Ra: oder Re:‘. Was wird gefunden?
2. Ich will die Zeichenkette ‚Foo:‘ am Zeilenanfang finden; allerdings
auch dann, wenn ihr noch der etwas folgt, also zum Beispiel ‚Foobar:‘.
Wie muss das Regex nach unserem bisherigen Stand aussehen?
3. Wir versuchen mal, ein Regex zu basteln, das die Zeichenfolge ‚Der‘ am Anfang einer Zeile oder ‚)‘ am Zeilenende findet.
4. Was bedeuten die folgenden Suchmuster?
a)“^“
b)“^x$“
c)“^$“
Im ersten Beispiel wird ‚Ra:‘ gefunden. Das hatten wir erwartet: gefunden wird die Alternative, die in der Zeichenkette zuerst erscheint.
Hoppla, das zweite Beispiel ist ja schon etwas
anspruchsvoller: „^(Foo|Foobar):“ Ok, man kann es vereinfachen:
„^Foo(|bar):“ Um nach nichts hinter dem ‚Foo‘ und vor dem ‚:‘ zu suchen,
haben wir auch einfach nichts als erste Alternative eingetragen. Wir
werden im folgenden Kapitel feststellen, dass man dieses auch anders
suchen kann.
Drittes Beispiel:
„(^Der|\)$)“ wäre die Lösung. Ihr hattet hoffentlich an den Backslash
vor der Klammer gedacht? Prima, dann habt ihr aufgepasst *g*. Probiert
es mal im Tester aus. Ihr werdet erkennen, dass das Regex aus der
Zeichenkette ‚Der Regen fällt (was aber keinen interessiert)‘ nur genau
‚Der‘ findet, denn da war die Bedingung erfüllt. Ändert den
Zeilenanfang, indem ihr irgendetwas eintragt und schon wird das ‚Der‘
nicht mehr gefunden. Stattdessen wird die abschließende runde Klammer
gefunden.
Viertes Beispiel:
Das erste Muster sucht nach allen Texten, die einen Zeilenanfang haben
oder am Zeilenanfang beginnen. Es werden also alle Strings, sogar der
leere String gefunden!
Das zweite Muster sucht nach Zeilen, in denen nur der Buchstabe x steht, direkt nach Zeilenbeginn und am Ende der Zeile.
Und das letzte Muster sucht einfach nach allen Texten, die Zeilenanfang
und -ende haben, aber nichts dazwischen. Es werden leere Zeilen gesucht.