Reguläre AusdrückePerl

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, ...

SeitenanfangSeitenendeBeschreibung 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";
}

# suche nach dem Muster world
if (/world/) { # suche world in $_
  print "Habe world gefunden";
}

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/;    # ersetze e durch a
$text=~s/wor/We/; # ersetze wor durch We
$text=~s/d/t/;    # ersetze d durch t

print $text."\n";
hello world
hallo Welt

SeitenanfangSeitenendeeinzelne 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:

# suche *hallo*
if ($text=~/\*hallo\*/){
  print "Habe *hallo* in der Zeichenkette $text gefunden!";
}

# suche 3+5
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/){
  # findet in $text maier, maler, mayer, mager, mauer, ...
  ...
}

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/){
  # findet maier, maler und mayer
  ...
}

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/){
  # findet nicht maxer, mawer und majer   ...
}

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]/) { # Zeichen
  ...
}

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.

# suche nach 3+5 oder 3-5
if ($text=~/3[\-+]5/){
  print "Habe 3+5 oder 3-5 gefunden";
}
Bemerkung: Die Listen der Sonderzeichen sind evtl. unvollständig.

SeitenanfangSeitenendeVielfachheit 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:

# beliebig viele l:
if (/hal*o/) {
  ...
}

# mindestens ein l:
if (/hal+o/) {
  ...
}

if (/hall*o/) { # auch mindestens ein l:
  ...
}

# ein oder kein l
if (/hal?o/) {
  ...
}

# vorgegebene Anzahl von l
if (/hal{4}o/) { # exakt viermal ein l: hallllo
  ...
}

if (/hal{2,5}o/) { # min. 2 und max. 5 l
  ...
}

if (/hal{3,}o/) { # min. 3 l
  ...
}

Mit obigen Multiplikatoren kann man einiges anstellen! Hier sind einige Beispiele aus den man lernen kann.

$temp="hallllllllllllllllllol";

$temp=~s/l/k/; # ersetze l durch k
# liefert haklllllllllllllllllol

$temp=~s/l+/mmm/;
  # ersetze l (min 1 aber bel. viele) durch mmm
# liefert hakmmmol
# Perl ist ganz schön gefräsig! Statt sich mit dem
# ersten l zu begnügen, holt es sich gleich alle!

$temp=~s/m*/xxxxx/; # möchte die mmm ersetzen
# liefert xxxxxhakmmmol

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+?//; # liefert: suuuper

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";
}

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:

if ($seite=~/<a\s+href=.*?>/) {
  print "Habe einen Link gefunden!\n";
}

SeitenanfangSeitenendeKlammerung 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}/){   # sucht nach all
  print "1: true\n";
}

if ($t=~/(al){2}/){ # sucht nach alal
  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}/){ # entspricht /l.l./
  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:

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.

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

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

SeitenanfangSeitenendeVerankern 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/; # findet Hans aber nicht Hanseat
$t=~/\bHaus/;   # findet Haus, Hausboot, ...
$t=~/irne\b/;   # findet Gluehbirne aber keine Birnen
$t=~/\Bfant\B/; # trifft Elefanten aber keinen Elefant

SeitenanfangSeitenendeOder

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/) {
  # Perl findet Perl
  print "Programmiersprache gefunden.";
}

SeitenanfangSeitenendeRangfolge

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";
}

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";
}

SeitenanfangSeitenendeVariablenersetzung

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!";
}

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";
}

SeitenanfangSeitenendeOptionen

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) {
  # Gross/Kleinbuchstaben werden nicht unterschieden
  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!
$t="A\nZ";

if ($t=~/A.Z/) {
  print"1. fand: A.Z\n";
}

if ($t=~/A.Z/s) {
  # "\n" wird nicht getrennt beachtet
  print"2. fand: A.Z\n";
}
2. fand: A.Z

$t="X Y";

if ($t=~/X
         Y/x){ # Leerzeichen vergessen
  print "1: ok\n";
}

if ($t=~/X
         \s
         Y/x){
  print "2: ok\n";
}
2: ok

$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
$t="Hans Wurscht";

$t=~s/(\w+)(\s+)(\w+)/$3.$2.$1/e;
print $t."\n";
Wurscht Hans

SeitenanfangSeitenendesplit() 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 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


Seitenanfang FehlermeldungHilfe zur Fehlermeldung © 2001-2003 Email an den AutorPerl, Lehrstuhl Mathe II, Uni Bayreuth