(Am Ende des Artikels ist ein Forum für Fragen und Ergänzungen angehängt.)
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.
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.
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 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 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 |
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.
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 ^$
.
Mit dem |
Operator können alternative Zeichengruppen formuliert
werden. (foo)|(bar)
passt demnach auf Strings, die entweder "foo"
oder "bar" enthalten.
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.
[ top ]