Regexp: So arbeiten Sie mit regulären Ausdrücken

, Stand: 03.03.2014  Tipp druckenDrucken Symbol Kommentare 2 Kommentare

Egal ob beim Programmieren, auf der Linux-Shell oder der Server-Wartung. Überall helfen Reguläre Ausdrücke beim Erkennen und Auswerten von Informationen. Hinter den Ausdrücken steckt eine Syntax, die auf den ersten Blick seltsam aussieht, aber jede Menge Macht über Zeichen und Textmuster bietet. Dieser Beitrag zeigt, wie Sie mit Regulären Ausdrücken arbeiten.

Wer nach einem Wort in einem Text sucht, verwendet eine Suchfunktion. Doch schon bei unterschiedlichen Wortformen wird es schwierig: Was ist, wenn man "Auto" und "Autos" sucht? Dann gibt man als Suchbegriff vielleicht nur "Auto" ein und bekommt zusätzlich "Automobile" oder "Autosuggestion". Das kann nicht die Lösung sein. Besser wäre, man könnte formulieren: "Suche nach dem Wort 'Auto' mit oder ohne 's' am Ende". Solche Formulierungen sind die Stärke von Regulären Ausdrücken. Ein Regulärer Ausdruck für die Auto-Suche wäre:

Autos?

Das Fragezeichen wirkt sich auf den direkt davor stehenden Buchstaben aus. Es bedeutet: Dieses Element darf genau nullmal oder einmal vorkommen. Damit findet die Suche alle "Auto" und "Autos". Allerdings gehören dazu immer noch "Automobile" und alle anderen Auto-Varianten, weil der Reguläre Ausdruck noch nicht weiß, dass er nur nach ganzen Wörtern suchen soll. Das sagen Sie ihm mit

\bAutos?\b

Das \b steht für eine Wortgrenze. Es wird also nur nach "Auto" und "Autos" gesucht, wenn diese beiden Begriffe als eigenes Wort vorkommen. Wenn Sie Autos mit großem und kleinem Anfangsbuchstaben finden wollen verwenden Sie

\b[Aa]utos?\b

Die eckige Klammer fasst eine Gruppe von Zeichen zusammen, die alternativ gelten. Als erster Buchstabe im Ausdruck darf also wahlweise ein großes oder kleines A stehen. In der Regel werden Sie diese Unterscheidung aber nicht allzu oft brauchen. Denn die meisten Regex-Parser haben einen Schalter, mit dem Sie die Unterscheidung von Groß- und Kleinschreibung an und ausschalten können.

Zeilenanfang und Ende

Neben den bereits erwähnten Wortgrenzen sind noch zwei weitere Operatoren hilfreich: das Caret oder Dächlein ^ für den Anfang und das Dollarzeichen $ für das Ende einer Zeile. Soll der Ausdruck zum Beispiel Zeilen finden, die mit "Anfang" beginnen, schreiben Sie

^Anfang

Das genügt schon. Soll eine komplette Zeile erkannt werden, schreiben Sie

^Anfang bis Ende$

Diese Zeile muss dann genau dem Muster entsprechen. Es dürfen in der Zeile keine weiteren Zeichen stehen.

Ziffernmuster erkennen

Ein weiteres Beispiel: Sie möchten Zahlen zwischen 1 und 99 in einem Text finden. Dann probieren Sie es mit diesem Ausdruck:

\b[0-9]?[0-9]\b

Die beiden \b kennen Sie bereits. Sie sorgen dafür, dass wirklich nur ein und zweistellige Zahlen gefunden werden. Anderenfalls würde der Ausdruck auch in Zahlenreihen wie 12345 mehrfach fündig. Auch die eckige Klammer kennen Sie bereits: Sie stellt mehrere Alternativen zur Verfügung. Damit alle Ziffern von 0 bis 9 zutreffen, müsste hier eigentlich stehen: [0123456789]. Zum Glück dürfen Sie das abkürzen, eben auf [0-9]. Der erste Teil des Ausdrucks [0-9]? liest sich also: Eine Ziffer von 0 bis 9 darf einmal oder nullmal vorkommen. Danach muss eine zweite Ziffer folgen.

Der Ausdruck lässt sich aber noch geschickter schreiben, indem Sie Quantifizierer benutzen. Die bestimmen, wie oft das zuvor geschriebene Muster zutreffen darf. Probieren Sie es mit

\b[0-9]{1,2}\b

Die beiden Zahlen in den geschweiften Klammern geben an, wie oft ein Muster mindestens vorkommen muss und wie oft es höchstens vorkommen darf. In diesem Beispiel verlangt der Ausdruck also mindestens eine Ziffer zwischen 0 und 9 und erlaubt maximal zwei Stellen.

Der Vorteil dieser Schreibweise wird klar, wenn Sie zum Beispiel nach vierstelligen Ziffern suchen, etwa in

\b[0-9]{1,2}\.[0-9]{1,2}\.[0-9]{2,4}\b

Hier werden Daten gefunden wie: 1.1.2000, 10.05.1965 oder 10.5.65. Vor dem ersten und zweiten Punkt sind jeweils zweistellige Ziffern erlaubt, nach dem letzten Punkt sind es mindestens zwei, höchstens vier.

Die Quantifier lassen sich auch noch anders anwenden:

{5} bedeutet, dass das vorangestellte Teilmuster genau fünfmal vorkommen muss

{5,} bedeutet, dass das vorangestellte Teilmuster mindestens fünfmal, aber auch beliebig häufiger vorkommen darf.

Ziffern und Buchstaben gemischt erkennen

Die eckigen Klammern haben noch mehr in sich. Sie können nicht auf Ziffern oder Buchstaben passen, sondern lassen sich auch kombinieren. Zum Beispiel so:

\b[0-9a-zA-z]\b

Dieses Muster erkennt jedes Wort, das Buchstaben oder Ziffern enthält. Andere Zeichen werden nicht erkannt. Soll auch noch ein Unterstrich _ erlaubt sein, schreiben Sie

\b[0-9a-zA-z_]\b

Bleiben noch die Umlaute. Die sollten Sie auch mit angeben. Dann sieht der Ausdruck so aus:

\b[0-9a-zA-z_ÄäÖöÜü]\b

Jetzt findet der Ausdruck auch Wörter mit Umlauten darin. Übrigens lässt sich diese Suche kombiniert mit einem Quantifizierer prima nutzen, um Wörter bestimmter Länge zu finden. Sollen alle Wörter mit fünf Buchstaben gefunden werden, nehmen Sie

\b[a-zA-z_ÄäÖöÜü]{5}\b

Mit Hilfe der eckigen Klammern fangen Sie auch elegant verschiedene Schreibweisen eines Wortes ab. Der Ausdruck

\bH[aä]fen\b

Liefert sowohl "Hafen" als auch die Mehrzahl "Häfen".

Oder-Operatoren

Sollen in einem Text unterschiedliche Begriffe zu einem Treffer führen, müssen Sie diese Begriffe mit ODER verknüpfen. Ein Beispiel:

\bGoogle|Yahoo|MSN\b

Dieses Muster findet wahlweise die Begriffe Google, Yahoo oder MSN.

Auch bei Problemen mit Umlauten hilft die Klammer. In vielen Texten finden Sie zum Beispiel aufgelöste Umlaute, aus ü wird dann ue. Um dieses Problem zu umgehen, suchen Sie alternativ nach ü oder ue:

\bH(ü|ue)tte\b

In diesem Fall müssen die beiden Alternativen in Klammern gesteckt werden. Anderenfalls würde der Ausdruck entweder auf oder auf uette passen. So aber findet der Ausdruck sowohl Hütte als auch Huette.

Negation und Gier

In manchen Fällen muss sichergestellt sein, dass bestimmte Zeichen in einer Fundstelle nicht vorkommen. Beispiel: Sie suchen nach allen Zeichenketten, die mit Ba beginnen, die aber im weiteren Verlauf keine Vokale enthalten. Dann schreiben Sie

\bBa[^aeiou]*\b

Dieser Ausdruck wird bei Ball fündig, bei Baum hingegen schlägt er fehl.

Allerdings hat dieser Ausdruck ein Problem. Er markiert noch Leerzeichen, Punkte, Kommata und alles andere mit, das auf ein gefundenes Wort folgt. Das geschieht, obwohl mit \b eine Wortgrenze definiert wurde. Der Grund dafür liegt im Sternchen *. Das ist nämlich "gierig": Es holt sich so viele Zeichen wie möglich, die zum Muster passen. Und die dem Wort folgenden Zeichen, inklusive Leerzeichen passen wunderbar zu einem Ausdruck, der lediglich die Vokale ausschließt. Das geht so weiter, bis der Ausdruck das nächste neue Wort findet.

Dieser Gier setzen Sie ein Ende, indem Sie hinter dem gierigen Sternchen ein Fragezeichen setzen:

\bBa[^aeiou]*?\b

Das Problem mit der Gier zeigt ein Problem der Negation auf: Die hat oft unerwünschte Nebeneffekte. Versuchen Sie daher besser, explizit zu schreiben, welche Zeichen Sie wünschen. Das ist besser als zu schreiben, welche Zeichen Sie nicht wünschen. In diesem Fall würde das zu einem recht langen Ausdruck führen:

\bBa[b-df-hj-np-tv-z]*\b

In den eckigen Klammern sind also alle Zeichen von b bis d, von f bis h und so weiter erlaubt. Die Vokale bleiben ausgeschlossen – ebenso alle anderen unerwünschten Zeichen.

Backreference oder Rückwärtsreferenz

Die Rückwärtsreferenz ist vor allem für das Suchen und Ersetzen wichtig. Mit Hilfe von Klammern markieren Sie einen Teil des Suchmusters und können sich darauf später wieder beziehen. Nehmen Sie einen Suchstring wie

^Artikelnummer: ([0-9]{8})$

Der sucht nach achtstelligen Artikelnummern in einer Liste. Die Nummern sind in Klammern gesetzt. Nun soll die Liste so umgebaut werden, dass 1. der String "Artikelnummer:" verschwindet und zweitens die Nummer um zwei vorangestellte Nullen auf zehn Stellen erweitert wird. Der Ersetzungsstring sieht so aus:

00\1

Ausprobieren können Sie dieses Suchen und Ersetzen mit dem Regex-Coach (siehe Kasten). Dazu tragen Sie das Suchmuster und in Target string ein paar Beispielzeilen mit Artikelnummern ein. Dann aktivieren Sie das Kästchen m, um die Liste als mehrzeilig zu markieren. Nach einem Klick auf Replace geben Sie den Ersetzungsstring ein. Und wenn Sie noch g aktivieren, erscheinen alle Ergebnisse der Suchen- und Ersetzen-Aktion unter Replacement result.

Regex mit Perl und PHP

Im Normalfall brauchen Sie eine Programmiersprache wie PHP oder Perl, um Suchen-und-Ersetzen-Aktionen mit Regulären Ausdrücken auszuführen. In Perl sähe die Syntax aus obigen Beispiel so aus:

$zeile =~ s/^Artikelnummer: ([0-9]{8})$/00\1/;

Das =~ führen Sie die Ersetzung mit dem Inhalt der Variablen $zeile aus und weist das Ergebnis gleich wieder dieser Variablen zu. Das s steht für Ersetzen. Zwischen dem ersten und dem zweiten Schrägstrich steht der Suchausdruck. Zwischen dem zweiten und dritten der Ausdruck zum Ersetzen. Ein einfaches Ersetzungsprogramm sähe zum Beispiel so aus:

#!/usr/bin/perl

while ($zeile = <STDIN>)

{

$zeile =~ s/^Artikelnummer: ([0-9]{8})$/00\1/;

print $zeile;

}

Seine Daten holt sich das Skript aus der Standardeingabe. Der Aufruf:

./regex.pl < artikel.dat

Soll das Ergebnis in einer anderen Datei landen, geben Sie ein:

./regex.pl < artikel.dat >neueartikel.dat

Auch PHP hat Regex-Fähigkeiten. Die nutzen Sie mit der Funktion preg_replace. Zum Beispiel:

<?

$arrArtikel = Array(

'Artikelnummer: 01234567',

 

'Artikelnummer: 12345678',

 

'Artikelnummer: 23456789');

foreach($arrArtikel as $strLine)

{

echo preg_replace('/^Artikelnummer: ([0-9]{8})$/', '00\\1', $strLine)."\n";

}

?>

Als ersten Parameter geben Sie den Suchteil an. Der muss, wie bei Perl, von Schrägstrichen eingerahmt sein. Der zweite Parameter ist der Ersetzungsstring. Wichtig: Statt des sonst üblichen einzelnen Backslash müssen hier zwei stehen. Der dritte Parameter enthält die Variable, die den zu behandelnden String enthält. Mehr zu preg_replace finden Sie unter www.php.net/preg_replace.

Reguläre Ausdrücke in der Praxis

In der täglichen Praxis begegnen Ihnen Reguläre Ausdrücke an jeder Ecke. Beispiel Log-Dateien. Sie möchten im Log live mitbeobachten, ob die Bots von Google, MSN und Yahoo auf Ihren Server zugreifen? Dann öffnen Sie die Linux-Shell Ihres Servers und geben Sie ein

tail -f log/access_w.log | egrep "Googlebot|Slurp|msnbot"

Der Ausdruck Googlebot|Slurp|msnbot zeigt alle Zeilen, die einen der drei Botnamen enthalten.

Etwas komplizierter wird es, wenn Sie wie im folgenden Beispiel alle HTML-Dateien im Log anzeigen wollen. Denn mit einem einfachen "grep html" ist es da nicht getan. Schließlich ist auch im Referer häufig die Dateiendung .html anzutreffen. Und das würde falsche Ergebnisse bringen.

Aber das soll kein Problem sein. Ein Blick ins typische Apache-Log zeigt, dass vor dem Namen der abgerufenen Seite ein "GET" steht. Dieses Merkmal machen Sie sich zunutze. Probieren Sie es mit

cat log/access_w.log | egrep "GET [^ ]+\.html"

Profis und Puristen dürfen gerne auch

egrep "GET [^ ]+\.html" < log/access_w.log

schreiben. Und wer den Überblick bewahren will, packt noch ein | less hinter die Zeile, damit er die Ausgabe seitenweise durchblättern kann:

egrep "GET [^ ]+\.html" < log/access_w.log | less

Der Reguläre Ausdruck sucht alle Zeilen heraus, in denen GET gefolgt von einem Leerzeichen steht. Danach folgt mindestens ein Zeichen (+), das kein Leerzeichen ist [^ ]. Und am Schluss der Fundstelle muss die Endung .html stehen. Das genügt schon.

Reguläre Ausdrücke für Apache

Ein anderer typischer Anwendungsbereich für reguläre Ausdrücke ist der Server Apache. In dessen Konfigurationdatei oder in der Datei .htaccess erkennen Sie mit Hilfe von Regulären Ausdrücken URLs und schreiben diese mit Hilfe des Moduls mod_rewrite um. Typischerweise passiert das bei dynamischen Webseiten, die gegenüber dem Web statische Adressen vorspiegeln. Aus einem

www.foo.bar/mein_toller_artikel_a123.html

wird dann intern ein

www.foo.bar/show.php?id=123

Der reguläre Ausdruck dazu sähe in der Datei .htaccess so aus:

RewriteEngine on

RewriteRule a([0-9]+)\.html$ /show.php?id=$1

Der Reguläre Ausdruck steckt in a([0-9]+)\.html$. Er sucht nach einer Zeichenkette, die mit a beginnt, dann eine ein- oder mehrstellige Zahl hat und auf .html endet. Das $-Zeichen steht für das Zeilenende. Die Zahl wird dabei in Klammern gepackt, im Ersetzungsteil mit $1 referenziert und an den Parameter id des PHP-Files show.php angehängt. Dieses $1 erfüllt denselben Zweck wie \1 bei anderen Regex-Maschinen. Der Benutzer bekommt von diesem Umschreiben nichts mit.

Ein weiteres Einsatzgebiet ist das Umleiten von Domainnamen. Die meisten Domains sind unter mindestens zwei Adressen zu erreichen. Einmal mit vorangestelltem www und einmal ohne. Wer dann noch alternative Domainnamen gebucht hat, um Schreibfehler abzufangen, hat ein großes Problem: Google betrachtet all diese Domänen extra und crawlt jede von ihnen. Die Folge ist früher oder später eine Strafe wegen doppelter Inhalte. Um das zu verhindern, tragen Sie dies in die .htaccess ein:

RewriteCond %{HTTP_HOST} !^www\.mysitexy\.de$

RewriteRule (.*) http://www.mysitexy.de/$1 [R=permanent]

Die erste Zeile wertet die aufgerufene Adresse aus. Das ! vor dem Regulären Ausdruck negiert diesen. Wenn also der Domain-Name NICHT www.mysitexy.de ist, greift die Umschreiberegel. Die nimmt sich einfach den kompletten Pfad und hängt diesen hinter den neuen Domainnamen. Wichtig: Die RewriteRule berücksichtigt nicht den Domainnamen, sondern nur den Pfad in der URL.

Das [R=permanent] markiert diese Umleitung als permanent – die Suchmaschine wird künftig den alten Domainnamen nicht mehr crawlen.

Metaoperatoren und weitere Spezialitäten

Meta-Operatoren in Regulären Ausdrücken ersparen eine Menge Schreibarbeit und machen den Code übersichtlicher. Wichtige Metaoperatoren sind:

\b steht für einen Wortanfang oder ein Wortende

\d steht für eine beliebige Dezimalzahl, entspricht [0-9]

\w steht für einen Buchstaben, Ziffer oder Unterstrich

\s steht für "Whitespace", also ein nicht druckbares Zeichen. Dazu zählen unter anderem Leerzeichen, Tabulator sowie Zeilenumbrüche

Mit \B, \D, \W und \S erreichen Sie jeweils das Gegenteil. \D zum Beispiel passt auf alle Zeichen, die keine Ziffern sind.

Zwei weitere Operatoren sind nützlich:

(?i) schaltet die Unterscheidung von Groß- und Kleinschreibung aus

(?-i) schaltet Unterscheidung von Groß- und Kleinschreibung wieder ein

In Perl und PHP brauchen Sie können Sie diese Unterscheidung zusätzlich über den Parameter i steuern, den Sie an den Suchausdruck anhängen, etwa

preg_replace('/haus/i', 'Hof', $strLine);

Web-Adressen

Regular Expressions Info * www.regular-expressions.info/ * Tutorial und viele Beispiele

Regenechsen * www.regenechsen.de/regex_de/ * Grundlagen zu Regulären Ausdrücken

PCRE * www.pcre.org/ * Alles über Perl-kompatible Reguläre Ausdrücke

2 Kommentare

Vielen Dank für diese tolle Anleitung! Die beste, die ich als
Anfänger bisher gefunden habe!
Viele Grüße
milode
[milode | 22.01.2014] Antworten

Hey, vielen Dank für diese super Anleitung! Eine der besten die ich
bis jetzt gefunden habe.
MFG BlackY =)
[BlackY | 30.10.2013] Antworten


Ihr Kommentar:


Name:


Aktualisiert am: 03.03.2014