Arbeiten mit Regulären Ausdrücken

(Am Ende des Artikels ist ein Forum für Fragen und Ergänzungen angehängt.)

2003-08-27 http://www.teamlinux.de/kb/script/regex?wid=180&func=view&pn=3

Artikel download regulaere_ausdruecke.zip (8 kb)

 

Reguläre Ausdrücke, im englischen "regular expressions" oder kurz "regex" genannt, sind Muster ("patterns") für Texte. Regex bieten die Möglichkeit, Texte flexibel zu beschreiben und damit eine Suche oder einen Ersetzungsvorgang zu steuern. Deshalb spielen sie in Text-Werkzeugen wie vi, sed, grep und vor allem der Scriptsprache Perl eine wichtige Rolle.

Einsatz Regulärer Ausdrücke

Probleme, die sich mit Regex lösen lassen, gibt es viele:

Die Lösungen zu diesen Problemen sind sprachlich recht einfach zu beschreiben:

Mit Regex können wir diese Beschreibungen in eine Symbolsyntax umsetzen. Diese Symbolsyntax muss uns die Möglichkeit geben, für jede Position im Text die möglichen Zeichen und deren Anzahl zu definieren. Mit anderen Worten, in einer Regex definieren wir für jede Position jeweils die Qualität und die Quantität möglicher Zeichen. Dafür werden sog. "Metazeichen" verwendet, also Zeichen mit einer Sonderbedeutung.

Der Prozess, eine Regex auf einen Text anzuwenden und zu prüfen, ob eine Übereinstimmung möglich ist, heißt im Englischen "Matching".

Muster aus Zeichen und Metazeichen kennen wir auch von den Dateinamens-Mustern, dem sog. "Globbing", der Linux-Shell. Hier finden wir Metazeichen wie *, ? und die eckigen Klammern. Qualität und Quantität werden hier aber vermischt. So steht * für "beliebige Zeichen" in "beliebiger Anzahl". Regex trennen dies aber konsequent.

 

Metazeichen

Der Backslash

Der Backslash \ ist ein Metazeichen, der einem folgenden Metazeichen seine Sonderbedeutung nimmt bzw. dem folgenden Standardzeichen eine Sonderbedeutung verleiht. Soll ein Backlash als Zeichen verstanden werden, muss er doppelt angegeben werden: \\

Qualitative Metazeichen

Qualitative Metazeichen beziehen sich auf die Art der an einer Position erlaubten Zeichen.

Zeichen Bedeutung
. (Punkt) beliebiges Zeichen ausser Newline (Zeilenende)
[123] Zeichenklasse, bestehend aus "1", "2" und "3"

In eckigen Klammern kann eine sog. "Zeichenklasse" definiert werden, die an der betreffenden Position eines der aufgeführten Zeichen erlaubt. Während das Formulieren einer Klasse [0123456789] für alle Ziffern noch akzeptabel ist, wäre die "Klasse aller Groß- und Kleinbuchstaben" unprakisch lang. Daher können auch Bereichsangaben gemacht werden: die Klasse [0-9a-zA-Z] umfasst alle Ziffern und Buchstaben (Umlaute sind Sonderzeichen und damit ausgenommen). Soll das Minuszeichen ebenfalls in der Klasse enthalten sein, schreibt man es einfach an den Anfang oder das Ende der Auflistung: [0-9+*/-] erlaubt Ziffern und Grundoperatoren.

Beispiel: Soll das Wort "TeamLinux" ungeachtet der richtigen Groß- und Kleinschreibung gefunden werden, eignet sich die Regex [tT]eam[lL]inux.

In Fällen, in denen es einfacher ist, zu definieren, was an der Position gerade nicht stehen darf, kann eine Zeichenklasse durch ein führendes ^ invertiert werden. [^0-9] erlaubt also gerade keine Ziffer, aber jedes andere Zeichen.

Zur Vereinfachung gibt es bereits vordefinierte Zeichenklassen.

Zeichenklasse Umfang
\d Alle Ziffern von 0 bis 9
\w Die "Wortzeichen" a bis z, A bis Z, 0 bis 9 und _
\n Das Newline-Zeichen (am Zeilenende)
\t Das Tabulatorzeichen
\s "Whitespace", d.h. Leerzeichen, Tabulator oder Newline

Auch die vordefinierten Klassen können einfach invertiert werden:

Zeichenklasse Umfang
\D Alle ausser Ziffern
\W Alle ausser "Wortzeichen"
\S Alle ausser Whitespace

TIPP: Die kombinierte Klasse [\d\D] umfasst somit wirklich alle Zeichen, also im Gegensatz zum .auch das Newline.

Quantitative Metazeichen

Quantitative Metazeichen definieren, wie oft das vorangehende (Meta-) Zeichen wiederholt werden darf.

Zeichen Bedeutung
? möglich (0-1 mal)
* beliebig oft (0-n mal)
+ mindestens einmal (1-n mal)
{n} genaue Anzahl (n mal)
{n,m} Anzahl-Bereich (n-m mal)

Beispiele:

Regex Bedeutung
Beispiel:? "Beispiel" oder "Beispiel:"
Regex-*Test "RegexTest", "Regex-Test", "Regex--Test", usw.
.+ beliebig lange Folge (mind. 1) beliebiger Zeichen
.* Beliebig lange Folge beliebiger Zeichen, darf auch ganz fehlen
Test [0-9]+ "Test 0", "Test 1", "Test 4711", ...
[a-z]{4} Zeichenfolge aus 4 Kleinbuchstaben
\d{2,4} 2- bis 4-stellige Zahl

Zeichengruppen

Mittels runder Klammern können einzelne (Meta-) Zeichen zu Gruppen zusammengefasst werden. Zum einen können sich auf solche Gruppen wiederum Quantifizierungs-Metazeichen beziehen, zum anderen kann nach dem Matching auf die passenden Zeichen aus dem Text zurückgegriffen werden.

Beispiel: ([A-Z]+/){3} passt auf drei Folgen aus Großbuchstaben, an die sich jeweils ein Slash anschließt.

Grenzzeichen

Die Regex fred passt auch auf "alfred" oder "frederick", da über vorangehende oder nachfolgende Zeichen nichts ausgesagt wird. Ein genaueres Ergebnis könnte man mit \Wfred\W erzielen. Dass die umgebenden Zeichen durch die \W "verschluckt" werden, kann aber ungelegen sein. Daher gibt es das Metazeichen \b, das eine "Wortgrenze" symbolisiert, ohne jedoch selbst Platzhalter für ein Zeichen im Text zu sein. Die richtige Lösung für die Suche nach dem Wort "fred" ist also \bfred\b.

Manchmal möchte man eine Regex auch am Anfang bzw. am Ende des zu prüfenden Texts "verankern", d.h. das Matching mitten im Text verhindern. Dies erlauben die Metazeichen ^ und $, die in der Regex nicht für ein Textzeichen stehen, sondern für "Anfang" bzw. "Ende". Die Regex ^\d+ prüft also, ob ein Text mit einer Zahl beginnt, und \s+$ meint Whitespace am Ende des Textes. Ein leerer Text erfüllt die Regex ^$.

Alternative Muster

Mit dem | Operator können alternative Zeichengruppen formuliert werden. (foo)|(bar) passt demnach auf Strings, die entweder "foo" oder "bar" enthalten.

Reguläre Ausdrücke bei sed

Mit Hilfe des "Stream EDitor" sed können Veränderungen an Textdateien, die gewöhnlich interaktiv mit einem Editor wie vi oder Emacs gemacht werden, automatisiert werden. Dazu werden dem Programm entsprechende Anweisungen gegeben, die die Änderungen beschreiben.

Die Vielfalt der sed-Kommandos kann der Dokumentation des Programms entnommen werden. Hier nur so viel: Jede Anweisung besteht aus einer optionalen "Adresse" und einem "Kommando".

Die "Adresse" beschreibt den Zeilenumfang, den eine Änderung betreffen soll und kann zum Beispiel als Zeilenbereich 17-19 angegeben. Eine Adresse kann aber auch ein Regulärer Ausdruck sein, der anhand eines Musters beschreibt, welche Zeilen zu bearbeiten sind.

"Kommandos" bestehen immer aus einem Buchstaben: i wie "insert" fügt eine oder mehrere Zeilen ein, d wie "delete" löscht vorhandene Zeilen. Beim Suchen und Ersetzen von Textteilen kommen auch hier wieder Reguläre Ausdrücke zum Einsatz.

sed kann seine Anweisungen auf 2 Arten erhalten:

sed -e «Anweisung» < foo.txt > bar.txt
sed -f anweisungen.txt < foo.txt > bar.txt

In der ersten Variante wird die Anweisung direkt auf der Kommandozeile mitgegeben. Dies ist auch mehrfach durch Wiederholung der -e Option möglich. Ab einer gewissen Anzahl von Anweisungen ist es aber sinnvoll, diese zeilenweise in eine Datei zu schreiben und sed entsprechend dem zweiten Beispiel aufzurufen.

Einige Beispiele für Anweisungen:

1-10d
20i\
Hallo!
/^[0-9]+/d
s/Windows/Linux/g

Die erste Anweisung löscht im Text die Zeilen 1 bis 10. Die zweite Anweisung fügt bei Zeile 20 die Zeile "Hallo!" ein. Die dritte Anweisung verwendet wieder das "delete" Kommando und enthält als Adresse einen Regulären Ausdruck, der in Slashes eingefasst ist. Hier werden alle die Zeilen gelöscht, die mit einer Ziffernfolge beginnen. Bei der vierten Anweisung fehlt eine Adresse, sie bezieht sich somit auf alle Zeilen im Text. Das "substitute" Kommando arbeitet mit zwei durch Slashes getrennten Regulären Ausdrücken, die Suchmuster und Ersetzungsmuster darstellen. Demnach wird hier jedes Vorkommen von "Windows" durch "Linux" ersetzt. Die abschließende Option g bewirkt, dass nicht nur das erste Vorkommen in jeder Zeile ersetzt wird, sondern jedes.