Reguläre Ausdrücke
Jetzt wird's heiß ...
Ein regulärer Ausdruck ist ein Muster, das mit einer Zeichenkette verglichen wird. Oft wird ein regulärer Ausdruck dazu benutzt, um etwas bestimmtes zu suchen (z. B. einen Link in einer HTML-Seite) oder man will einen Teil einer Zeichenkette verändern, ...

Beschreibung und Beispiele
Gekennzeichnet wird ein regulärer Ausdruck mit zwei // - alles was zwischen den beiden Slashes steht, ist das Muster.
Will man in einem String $text schauen, ob der Text hello enthält, sucht man nach dem Muster hello, wie folgt:
if ($text=~/hello/) {
print "Habe hello gefunden!\n";
}
if (/world/) {
print "Habe world gefunden";
}
- Dieser sog. Match-Operator (//) liefert true, falls das Muster gefunden wurde, und false, sonst.
- Mit dem Operator =~ kann man dem regulären Ausdruck sagen, wo das Muster gesucht werden soll.
- Auch bei regulären Ausdrücken wird die Spezialvariable $_ verwendet: Wird dem regulären Ausdruck keine Variable zugeordnet, so sucht Perl automatisch in der Variablen $_.
Neben dem Suchen kann man auch eine Ersetzung vornehmen und zwar mit dem Substitute-Operator: s/muster/ersetzen mit/;:
$text="hello world";
print $text."\n";
$text=~s/e/a/;
$text=~s/wor/We/;
$text=~s/d/t/;
print $text."\n";
hello world
hallo Welt

einzelne Zeichen und Klassen von Zeichen
Die Zeichen \ | ( ) [ { ^ $ * + ? . / sind Sonderzeichen innerhalb eines regulären Ausdrucks (diese werden später noch erklärt). Wenn man diese in einem String sucht, so muss man einen Backslash \ davor setzen:
if ($text=~/\*hallo\*/){
print "Habe *hallo* in der Zeichenkette $text gefunden!";
}
if ($text=~/3\+5/){
print "Habe 3+5 in der Zeichenkette $text gefunden!";
}
Normalerweise weiß man nicht exakt, welche Zeichen vorhanden sind. Bleiben wir bei dem Link in einer HTML-Seite: Dieser beginnt mit <a, dann kommt die Referenz (die man vielleicht kennt) und sonst irgendwas, bis zum >. Man weiß also recht wenig. Perl hat für Zeichen, die man nicht unbedingt kennt, den . eingeführt. . bedeutet: 'irgendein beliebiges Zeichen außer dem Newline ("\n")':
if ($text=~/ma.er/){
...
}
Jetzt können wir beliebige Zeichen darstellen. Wenn man eine bestimmte Auswahl von Zeichen vorgeben will (man will z. B. nur maler, maier und mayer finden), dann gibt es auch dafür eine Lösung. Man gibt eine sogenannte Zeichenklasse an, in der alle zulässigen Zeichen stehen. Sie wird mit [ eingeleitet und mit ] beendet:
if ($text=~/ma[liy]er/){
...
}
Wie der . genau ein Zeichen repräsentiert, so auch eine Zeichenklasse: Genau ein Zeichen aus dem angegeben Möglichkeiten.
Will man eine Auswahl von Zeichen nicht haben (Negation), stellt man nach dem [ das Zeichen ^:
if ($text=~/ma[^xwj]er/){
...
}
Um sich etwas Schreibarbeit zu sparen und natürlich auch die Übersicht zu behalten, kann man etwas wie [012345] auch schreiben als [0-5]. Das gilt auch für Buchstaben: statt [cdefghijklmnop] schreibt man kurz [c-p]. Folgendes ist auch möglich:
if ($text=~/[a-zA-Z_0-9]/) {
...
}
Es gibt spezielle Kürzel innerhalb der der regulären Ausdrücke - so wie der . -, die aber nur bestimmte Zeichen abdecken:
Kürzel |
Entsprechung als Zeichenklasse |
Beschreibung |
. |
|
alle Zeichen außer "\n" |
\d |
[0-9] |
eine Ziffer |
\D |
[^0-9] |
keine Ziffer |
\w |
[a-zA-Z0-9_] |
Buchstabe, Ziffer oder _ |
\W |
[^a-zA-Z0-9_] |
weder Buchstabe noch Ziffer noch _ |
\s |
[ \n\t\r\f] |
Sonderzeichen: Leerzeichen, Neuezeile, ... |
\S |
[^ \n\t\r\f] |
keines der Sonderzeichen |
Beispiele:
$t='Ich bin 77 Jahre';
if ($t=~/\d/) {
print "Ziffer enthalten\n";
}
if ($t=~/\w/) {
print "Zeichen enthalten\n";
}
if ($t=~/\s\d\s/) {
print "einstellige Zahl enthalten.";
}
Die Sonderzeichen, die im regulären Ausdruck (s. o.: \ | ( ) [ { ^ $ * + ? . /) außerhalb der Zeichenklasse stehen, sind andere als die, die in der Zeichenklasse vorkommen. Letztere sind: $ - ] \ / ^. Auch diese werden mit Hilfe von einem Backslash \ darstellbar gemacht. Alle anderen Zeichen werden in der Zeichenklasse auch so interpretiert; das gilt auch für den .; d.h., dass /[,.]/ nach einem , oder einem . in der Zeichenkette sucht.
if ($text=~/3[\-+]5/){
print "Habe 3+5 oder 3-5 gefunden";
}

Vielfachheit bzw. Multiplikatoren
Will man nun testen, ob ein Wort in der Zeichenkette ist der Form: hao oder halo oder hallo oder halllo oder hallllo oder ... hallo mit beliebig vielen l in der Mitte, dann benutzt man einen * im regulären Ausdruck:
if (/hal*o/) {
...
}
if (/hal+o/) {
...
}
if (/hall*o/) {
...
}
if (/hal?o/) {
...
}
if (/hal{4}o/) {
...
}
if (/hal{2,5}o/) {
...
}
if (/hal{3,}o/) {
...
}
- * bedeutet: das vorhergegangene Zeichen ist beliebig oft vorhanden.
- Das vorhergegangene Zeichen mindestens einmal vorhanden durch +.
- Das Fragezeichen ? testet, ob das vorangegangene Zeichen genau einmal oder gar nicht enthalten ist.
- {zahl} bedeutet: Genau zahl-mal darf das vorhergehende Zeichen vorkommen.
- Mit {min,max} kann man einen Bereich der Vielfachheit angeben: von min bis max. Lässt man die Angabe von max weg (das Komma bleibt), so wird nur eine untere Grenze angegeben.
Mit obigen Multiplikatoren kann man einiges anstellen! Hier sind einige Beispiele aus den man lernen kann.
$temp="hallllllllllllllllllol";
$temp=~s/l/k/;
$temp=~s/l+/mmm/;
$temp=~s/m*/xxxxx/;
- Alles klar?
- Ok - im letzten Beispiel kommen nur ???! Aber dafür gibt es eine Erklärung: /m*/ heißt beliebig viele m. Perl nimmt sich den String hakmmmol und fängt an mit dem Mustervergleich vor dem h und das Muster passt, weil kein m da ist! Also wird eingesetzt.
Würde man statt /m*/ /m+/ schreiben, sucht Perl nach mindestens einem m; d. h., im String wäre dann hakxxxxxol. Daran kann man erkennen, dass Perl recht gefräßig ist. Gibt man ein + oder * an, so holt sich Perl, falls die erste Übereinstimmung gefunden wurde, soviel, wie nur geht. Man kann diese Gefräßigkeit (ab der Version 5) unterbinden, indem man nach dem + oder * ein ? setzt:
$text='Perl ist suuuuper';
$text=~s/u+?//;
Ein gutes Beispiel für das ? liefert unsere HTML-Referenz <a href="www.mathe2.uni-bayreuth/perl/start.htm" target="_blank">.
Wir möchten einen String, wie z. B. "www.mathe2.uni-bayreuth/perl/start.htm", finden. Das heißt, wir suchen ein " anschließend beliebig viele Zeichen bis zum nächsten ":
$text='"www.mathe2.uni-bayreuth/perl/start.htm"'.
' ... "www.mathe2.uni-bayreuth/perl/inhalt.htm"';
if ($text=~/".*?"/) {
print "Habe String gefunden!\n";
}
- Hier muss das ? stehen, sonst findet Perl die beiden Strings"www.mathe2.uni-bayreuth/perl/start.htm" ... "www.mathe2.uni-bayreuth/perl/inhalt.htm".
Ein weiteres Beispiel: Wir möchten wissen, ob in der Seite, die in der Variablen $seite gespeichert ist, ein Link existiert. Kriterien dafür sollen sein:
- Anfang eines Links: <a.
- Nach dem <a soll mindestens ein Whitespace ( , \n, \t, ...) sein.
- Danach das Schlüsselwort href und ein =.
- Es können beliebig viele Zeichen bis zum Ende des Links folgen.
- Das Ende eines Links ist mit > gekennzeichnet.
if ($seite=~/<a\s+href=.*?>/) {
print "Habe einen Link gefunden!\n";
}
- Also ganz langsam:
- Als erstes suchen wir: <a: also /<a/,
- anschließend mindestens ein Whitespace \s+: /<a\s+/;
- dann kommt das HTML-Schlüsselwort href=: /<a\s+href=/;
- der Rest sollen beliebig viele Zeichen sein, bis ein > kommt; dabei muss aber beim ersten gefundenen > abgebrochen werden: .*?; also: /<a\s+href=.*?/.
- Das letzte Zeichen ist das >: /<a\s+href=.*?>/.

Klammerung und die Variable $1 bzw. \1
Mit Hilfe von runden Klammern () können wir uns auf mehr als ein Zeichen in regulären Ausdrücken beziehen, falls man zum Beispiel einen längeren Ausdruck mehrfach sucht:
$t="halali";
if ($t=~/al{2}/){
print "1: true\n";
}
if ($t=~/(al){2}/){
print "2: true\n";
}
2: true
Mit runden Klammern kann man nicht nur beliebige Zeichen, sondern auch einen beliebigen Teil des regulären Ausdrucks zusammenfassen:
$t="halali";
if ($t=~/(l.){2}/){
print "true\n";
}
true
Es gibt noch einen Nebeneffekt: Perl speichert die gefunden Ergebnisse beim Mustervergleich ab und zwar in die Variablen: $1 für die erste Klammerung, $2 für die zweite Klammerung, etc. Es zählt dabei die linke Klammer ( der Klammerung; bei Verschachtelung kann es dann keine Schwierigkeiten geben:
$t="Name: Hans\n".
"Groesse: 1.78 m\n";
if ($t=~/(\w+): ([\d.]+)/) {
print "$1 == $2\n";
}
Groesse == 1.78
Das passiert:
- Der reguläre Ausdruck passt auf: Ein Wort mit einem Doppelpunkt und einem Leerzeichen. Anschließend muss eine Zahl (Dezimalzahl möglich wegen .) kommen.
- Die gefundenen Werte in den Klammern werden den Variablen $1 und $2 zugewiesen und anschließend ausgedruckt.
Es ist sogar möglich, schon innerhalb des regulären Ausdrucks auf den Inhalt von $1, etc. zuzugreifen. Jedoch muss dort statt dem $-Zeichen der Backslash \ verwendet werden: also z. B.: \1:
Wir suchen in einem String zwei gleiche Zeichen hintereinander und möchten dieses ausgeben:
$t="Guten Morgen, Herr Mueller";
if ($t=~/(.)\1/) {
print "Das erste doppelte Zeichen ist $1.\n";
}
Das erste doppelte Zeichen ist r.
- Es wird ein beliebiges Zeichen - außer dem "\n" gesucht: .
- Dieses Zeichen wird in $1 bzw. \1 zwischengespeichert.
- Anschließend nach diesem Zeichen wird wieder dieses Zeichen gesucht wegen \1.
- Das erste Mal, wo das zutrifft, ist bei Herr.
Im Zusammenhang mit \1, etc. kann es zu Schwierigkeiten kommen (vgl. Oktalzahl):
$t="20.01.2001";
if ($t=~/(20).*\101/) {
print "1 ok";
}
if ($t=~/(20).*(\1)01/) {
print "2 ok";
}
2 ok
- Der erste reguläre Ausdruck trifft nicht zu, da \101 als Oktalzeichen interpretiert wird! Der Ausweg man klammert das \1 einfach ein.
Will man verhindern, dass der Wert einer Klammerung einer Variablen z. B. $1 zugewiesen wird, so muss man ein Fragezeichen mit einem Doppelpunkt ?: nach der Klammer setzen:
$t="12.02.2001";
$t=~/(?:\d+)\.(\d+)\.(\d+)/;
print "$1 - $2\n";
02 - 2001

Verankern eines Zeichenmusters
Bis war es nur möglich irgendwo im String schauen, ob das Muster passt. Will man dies sofort am Anfang testen, so muss man ein ^ als erstes Zeichen im regulären Ausdruck setzen (das nennt man dann auch Anker):
$t="name: Hans Wurscht";
if ($t=~/^name/) {
print "Der $t fängt mit 'name' an.\n";
}
Analog kann man einen Mustervergleich am Ende eines Strings durchführen mit einem $ am Ende des regulären Ausdrucks:
$t="und tschues, in italiano: e ciao"
if ($t=~/ciao$/) {
print "Das Dokument endet mit einem Gruss!\n";
}
Statt ^ und $ kann man auch fast immer \A und \Z verwenden. Der einzige Unterschied ergibt sich bei der Option m - siehe unten.
Es gibt noch etwas spezielles. Mit \b kann man festlegen, dass an dieser Stelle eine Wortgrenze vorliegt. Das ist ein Übergang von einem Wort (also \w) zu einem Nichtwort (\W) bzw. ein Übergang von einem Nichtwort zu einem Wort. \b kann aber auch Stringanfang oder Stringende bedeuten. Dabei repräsentiert \b kein Zeichen! Mit \B ist wieder das Gegenteil gemeint. Hier ein paar Beispiele:
$t=~/\bHans\b/;
$t=~/\bHaus/;
$t=~/irne\b/;
$t=~/\Bfant\B/;

Oder
Mit dem Oder-Operator | kann man Perl in einem regulären Ausdruck mehrere Alternativen zur Verfügung stellen:
$t="Ein Perlprogramm ist kein Pascalprogramm";
if ($=~/C|Pascal|Perl/) {
print "Programmiersprache gefunden.";
}
- Eine Alternative kann auch aus mehreren Zeichen bestehen.
- Perl durchläuft den String und schaut, ob das Muster passt. (Nicht umgekehrt!) Das heißt, der String wird Zeichen für Zeichen durchlaufen und es wird geschaut, ob das Muster auf eine der Alternativen passt.

Rangfolge
Auch in regulären Ausdrücken ist es möglich, dass es nicht eindeutige Ausdrücke gibt (analog zu dem Problem: Was ist 3+5*7). Eigentlich ist schon im Beispiel vorher unklar, ob Perl nach C, Pascal oder Perl sucht oder nach CascalerlDatenbankanbindungen, CascaPerl, Pascalerl oder PascaPerl. Deshalb wurde auch innerhalb von regulären Ausdrücken eine Rangfolge festgelegt:
Name |
Operator |
runde Klammern |
() |
Multiplikatoren |
+ * ? {m} {m,n} {m,} |
Zeichen, Anker |
abc123.[irgendwas]\d\w\s ^ \b \B $ |
Oder, Alternative |
| |
|
|
Damit ist auch geklärt, warum das Beispiel mit den Programmiersprachen auch so funktioniert, wie es soll: Zeichen bzw. ganze Zeichenfolgen haben höheren Rang als der Oder-Operator. Noch ein Beispiel:
$t="Name: Alfred";
if ($t=~/Name: (A.+?\b|E.+?\b|I.+?\b|O.+?\b|U.+?\b)/) {
print "Dieser Name beginnt mit einem Vokal: $1";
}
- Das schaut schlimmer aus als es ist.
- Also zuerst wird Name: gesucht;
- anschließend das, was innerhalb der Klammern steht: Entweder A und irgendwas bis eine Wortgrenze (\b) kommt oder E und irgendwas bis eine Wortgrenze kommt oder etc.
- Folgendes fällt noch auf: Man kann auf den Inhalt der Klammern zugreifen.
Kürzer geht's natürlich so ...
$t="Name: Alfred";
if ($t=~/Name: ((A|E|I|O|U).+?\b/)) {
print "Dieser Name beginnt mit einem Vokal: $1";
}
Und noch kürzer geht's so (leider ohne oder) ...
$t="Name: Alfred";
if ($t=~/Name: ([AEIOU].+)\b/)) {
print "Dieser Name beginnt mit einem Vokal: $1";
}

Variablenersetzung
In einem regulären Ausdruck können auch Variablen eingesetzt werden. Diese werden bevor gesucht wird, ersetzt mit dem Inhalt der Variablen. Wenn ich also nach einem Muster \d+ suchen will, kann ich das auch so tun:
$Number='\\d+';
$text="H2SO4";
if ($text=~/$Number/) {
print "Habe Zahl gefunden!";
}
- Perl ersetzt zuerst die Variable $Number durch deren Inhalt. Das heißt, Perl führt jetzt folgendes durch: $text=~/\d+/.
Obiges Beispiel noch einmal (etwas umständlich), um zu zeigen, was möglich ist:
%ww=(
a => 'A.+\b',
e => 'E.+\b',
i => 'I.+\b',
o => 'O.+\b',
u => 'U.+\b'
);
$t="Name: Alfred";
if ($t=~/Name: ($ww{a}|$ww{e}|$ww{i}|$ww{o}|$ww{u})/) {
print "Dieser Name beginnt mit einem Vokal: $1";
}
- Dies Beispiel liefert das gleiche Ergebnis wie oben.
- Es werden Elemente aus einem Hash - das sind wieder skalare Variablen - verwendet. Funktioniert auch.

Optionen
Einem regulären Ausdruck können auch Optionen übergeben werden. Dadurch wird das Verhalten z. B. beim Suchen oder Ersetzen verändert. Hier eine Tabelle:
Option |
Beschreibung |
i |
Groß- und Kleinschreibung ignorieren (case-insensitive) |
g |
globale Suche (global) |
s |
das Sonderzeichen \n wird nicht getrennt betrachtet (single line) |
x |
ermöglicht, komplizierte reguläre Ausdrücke - was ziemlich schnell geht - über mehrere Zeile auszudehnen und Kommentare einzufügen (extended) |
m |
Perl betrachtet ^ und $ nicht nur für Stringanfang und -ende, sondern auch für Zeilenanfang und -ende (multiple lines). Für \A und \Z hat diese Option keine Auswirkungen. |
e |
diese Option kann nur beim Ersetzen (s///) in einem String verwendet werden; der zweite Teil im Substitute-Operator also alles zwischen zweiten und dritten Slash / wird dann nicht als Zeichenkette interpretiert, sondern als (evaluate) |
Beispiele dazu:
$t="Kunigunde Fritz";
if ($t=~/kunigunde/i) {
print "Hi, Kunigunde\n";
}
Hi, Kunigunde
$t="Hallo, Leute - in dieser Variablen sind viele l's enthalten!";
print $t."\n";
$i=0;
while ($t=~/l/g) {
$i++;
print $i." ";
}
print "\n";
$j=0;
while ($t=~/l/gi) {
$j++;
print $j." ";
}
print "\n\n";
$t=~s/(l)/<$1>/gi;
print "$t\n";
Hallo, Leute - in dieser Variablen sind viele l's enthalten!
1 2 3 4 5 6
1 2 3 4 5 6 7
Ha<l><l>o, <L>eute - in dieser Variab<l>en sind vie<l>e <l>'s entha<l>ten!
- Mit Hilfe der Option g und der while-Schleife werden alle l gefunden.
- Es sind auch mehrere Optionen möglich. In der zweiten while-Schleife wird ein l mehr gefunden. Es wurde die Option i mit angegeben. Damit fand Perl auch das L.
- Wendet man die Option g auf den Substitutions-Operator an, so wird jedes gefundene Muster ersetzt.
$t="A\nZ";
if ($t=~/A.Z/) {
print"1. fand: A.Z\n";
}
if ($t=~/A.Z/s) {
print"2. fand: A.Z\n";
}
2. fand: A.Z
- Mit der Option s wird das Neuezeile-Zeichen bezogen auf den . wie jedes andere Zeichen behandelt.
$t="X Y";
if ($t=~/X
Y/x){
print "1: ok\n";
}
if ($t=~/X
\s
Y/x){
print "2: ok\n";
}
2: ok
- Wenn man die Option x angibt, kann man einen regulären Ausdruck über mehrere Zeilen verteilen.
- Es kann Schwierigkeiten mit Leerzeichen geben.
$t="Apfel\nBirnen\nKuchen";
@essen1=($t=~/^\w+/g);
foreach (@essen1) {
print "1: ".$_." ";
}
print "\n";
@essen2=($t=~/^\w+/mg);
foreach (@essen2) {
print "2: ".$_." ";
}
1: Apfel
2: Apfel 2: Birnen 2: Kuchen
- Man kann das Ergebnis eines regulären Ausdrucks auch an ein Array weitergeben. Dabei muss aber, darauf geachtet werden, dass global gesucht wird.
- Im zweiten Teil wurde die Option m angegeben. Es werden deshalb alle Wörter (\w+) am Anfang jeder Zeile (^) gefunden.
- Die Option s hebt diese Vorgehensweise nicht auf; d. h. ^ bzw. $ in Kombination mit der Option m ist auch dann möglich und sinnvoll.
$t="Hans Wurscht";
$t=~s/(\w+)(\s+)(\w+)/$3.$2.$1/e;
print $t."\n";
Wurscht Hans
- In der zweiten Hälfte des Substitute-Operators ist jetz keine Zeichenkette angegeben, sondern es können richtige Operationen ausgeführt werden. Wie z. B. Verknüpfen von Strings.

split() und join()
Der Operator split() zerlegt mit Hilfe eines regulären Ausdrucks eine Zeichenkette. Zurückgegeben wird ein Array. Die Syntax lautet split(/regulärer Ausdruck/,Zeichenkette):
$path='C:\Programme\WWW\Perl';
@verzeichnisse=split(/\\/,$path);
foreach (@verzeichnisse) {
print "$_\n";
}
C:
Programme
WWW
Perl
- Der split-Operator liefert die Zeichenketten, die zwischen den Backslashes \ stehen.
Der Operator join() verknüpft Zeichenketten. Die Syntax lautet join(Zeichenkette, Array). Dabei werden die Elemente des Arrays zusammengeklebt. Zwischen den einzelnen Elementen steht aber die Zeichenkette. Als Ergebnis liefert der join-Operator einen String:
@Namen=('Fritz','Franz','Georg','Hans');
$erg=join('---',@Namen);
print "$erg\n";
Fritz---Franz---Georg---Hans
- Der erste Parameter, der dem join-Operator übergeben wird, ist kein regulärer Ausdruck, sondern eine Zeichenkette.