Regular Expressions (Reguläre Ausdrücke)

Einleitung

Reguläre Ausdrücke (englisch: regular expressions; kurz: regex) dienen dem Finden und Bearbeiten von Zeichenmustern. Dieses Muster muss natürlich einem bestimmten Syntax folgen, doch leider gibt es keinen „Standard-Syntax“ für reguläre Ausdrücke. Dieses Tutorial soll eine Einführung in reguläre Ausdrücke der Programmiersprache Perl bieten.

Einfacher Vergleich mit exaktem Muster

Wir können auch eine exakte Zeichenkette als Muster verwenden. Das macht zwar noch nicht viel Sinn, aber wir fangen mal klein an :-)
Nehmen wir folgenden Text als Beispiel:

Das ist der Text für unseren ersten regulären Ausdruck!

Darauf passt das Muster

er

genau 3 mal. Besser gesagt: Wir haben 3 „Matches“, nämlich in den Wörtern „der“, „unseren“ und „ersten“.

Zeichenklassen

Zeichenklassen erlauben das vorkommen von mehreren verschiedenen Zeichen an einer Stelle. Dazu werden die möglichen Zeichen zwischen eckige Klammern gesetzt:

[JL]ava

ist ein regulärer Ausdruck bei dem wir sowohl für „Lava“ als auch „Java“ eine Übereinstimmung erhalten. Wichtig ist, dass es zwischen den Zeichen kein Trennzeichen wie etwa ein Leerzeichen oder einen Beistrich gibt, da ansonsten diese selbst als Zeichen in die Zeichenklasse aufgenommen werden. Regular Expressions sind case-sensitive, d.h. unser vorheriger Ausdruck trifft nicht auf „lava“ oder „java“ zu. Das können wir leicht ändern, indem wir die kleinen Buchstaben ebenfalls in die Zeichenklasse aufnehmen:

[JjLl]ava

Nun wollen wir einen regulären Ausdruck erstellen, der auf jedes klein geschriebene Zeichenfolge mit 3 Buchstaben zutrifft. Nach unserem bisherigen Wissensstand würde das so aussehen:

[abcdefghijklmnopqrstuvwxyz][abcdefghijklmnopqrstuvwxyz][abcdefghijklmnopqrstuvwxyz]

Alle Elemente der Zeichenklasse liegen in einer Reihenfolge, da ist es doch ziemlich aufwändig sie jedes mal alle aufzuzählen. Deshalb können wir um einen Zeichenbereich festzulegen den Bindestrich verwenden:

[a-z][a-z][a-z]

Gleiches gilt natürlich für Ziffern:

[0-9][0-9][0-9]

Dieser Ausdruck trifft auf alle 3-stelligen Zahlenfolgen zu. Wollen wir den Bindestrich als Teil der Zeichenklasse verwenden müssen wir davor einen Backslash setzen:

[a\-z]

Hier wird kein Bereich angegeben, sondern eine Zeichenklasse mit den 3 einzelnen Buchstaben 'a', '-' und 'z'.
Indem wir ein Zirkumflex (^) an den Beginn einer Zeichenklasse stellen, negieren wir sie. Somit die trifft die Zeichenklasse auf alle Zeichen, außer jenen die in ihr definiert sind.

[^a-zA-Z]

trifft also auf alle Zeichen zu, die keine Buchstaben sind. Für das Zirkumflex gilt das selbe wie für den Bindestrich: Soll es ein Zeichen der Zeichenklasse sein, muss ein Backslash davor gesetzt werden.
Da diese Zeichenklassen so gebräuchlich sind, gibt es Abkürzungen für sie:

Zeichenklasse Abkürzung
Buchstaben, Ziffern und Unterstrich ([a-zA-Z_0-9]) \w
Alles außer Buchstaben, Ziffern und Unterstrich ([^a-zA-Z_0-9]) \W
Ziffern ([0-9]) \d
Alles außer Ziffern ([^0-9]) \D
Whitespaces ([ \n\t…]) \s
Alles außer Whitespaces ([^ \n\t…]) \S
Jedes Zeichen außer Newline .

Quantoren

Noch einmal zurück zu unserem Beispiel:

[a-z][a-z][a-z]

Wir haben uns zwar schon eine Menge an Schreibarbeit durch die Verwendung von Abkürzungen erspart, trotzdem ist unser Muster noch redundant. Um auch dieses Problem zu lösen können wir mit Hilfe von Quantoren genau angeben wie oft ein Zeichen bzw. eine Zeichenklasse vorkommen kann oder muss:

Quantor Bedeutung
{n} Muss genau n mal vorkommen
{n, } Muss mindestens n mal vorkommen
{n, m} Muss mindestens n und maximal m mal vorkommen
* Kann beliebig oft vorkommen
+ Kann beliebig oft, muss aber mindestens ein mal vorkommen

Dadurch können wir unseren Ausdruck nochmal kürzen:

[a-z]{3}

Quantoren sind von Natur aus gierig, d.h. sie nehmen so viel Platz ein wie nur möglich. Mit folgenden Ausdruck wollen wir einen HTML-Tag finden:

<.+>

Soweit so gut. Nehmen wir ein Beispiel:

<h1><font color=#0000FF>proggen.org</font></h1>

Zuerst könnte man annehmen wir bekommen folgende Matches:

<h1>
<font color=#0000FF>
</font>
</h1>

Wenn wir aber bedenken, dass der Quantor '+' so viel Platz wie nur möglich in Anspruch nimmt, merken wir, dass wir nur einen einzigen Match haben:

<h1><font color=#0000FF>proggen.org</font></h1>

Ja, '+' beschlagnahmt kompletten Text vom ersten '<' bis zum letzten '>'. Dieses Verhalten können wir ändern, indem wir ein Fragezeichen nach einem Quantor schreiben:

<.+?>

Führt zur erwarteten Ausgabe.

Gruppen

Wir können Zeichenketten und -klassen auch zu einer Gruppe zusammenfassen. Dazu setzen wir sie einfach zwischen runde Klammern:

([pP]erl)

Damit haben wir bereits eine einfache Gruppe erstellt. Im nachfolgenden Teil des regulären Ausdrucks können wir uns jetzt wieder mit einer Rück-Referenz auf diese Gruppe beziehen. Um sich auf eine Gruppe zu beziehen wird entweder \g{index} mit dem Index der Gruppe (beginnend bei 1!) geschrieben oder direkt der Index als Escape-Sequenz. Negative Indizes beginnen von hinten zu zählen. Folgende reguläre Ausdrücke zum Finden des Inhalts eines HTML-Tags (Ergebnis in Gruppe 2) sind also äquivalent:

<(.+?)>(.*)</\g{1}>
<(.+?)>(.*)</\g{-2}>
<(.+?)>(.*)</\1>

Gruppen kann man auch Namen geben, über den sie später schnell wieder gefunden werden kann:

<(?<Tag>.+?)>(.*)</\k<Tag>>

Wie im Beispiel demonstriert erfolgt Zuweisung über (?<name>) und die Dereferenzierung durch \k<name>.
Es ist ebenfalls möglich Bedingungen in Gruppen zu formulieren, dazu wird das in vielen Programmiersprachen als „oder“-verwendete Zeichen '|' innerhalb einer Gruppe geschrieben.

(Perl|Python) gefällt mir (sehr gut|nicht).

Dieses Muster passt auf folgende Texte:

Perl gefällt mir sehr gut.
Python gefällt mir sehr gut.
Perl gefällt mir nicht.
Python gefällt mir nicht.

Weitere Meta-Zeichen und Escape-Sequenzen

Meta-Zeichen Bedeutung
^ Beginn der Zeile
$ Ende der Zeile


Escape-Sequenz Bedeutung
\b Beginn oder Ende eines Wortes
\B Alles außer der Beginn oder Ende eines Wortes