Wir fangen mit dem einfachsten Beispiel an: Die Suche nach einem bestimmten Zeichen:
|
Hier ist der reguläre Ausdruck nur der Buchstabe "e
"
und der Operator "=~
" veranlaßt eine Suche nach
diesem Zeichen in dem String $t
. Diese Suche beginnt immer am
Anfang einer Zeichenkette, so daß sie schon beim zweiten Zeichen
erfolgreich ist (Demo-Zeichenkette
); die Ausgabe
des Programms ist also "wahr
".
Anstelle eines Zeichens können auch beliebig viele gesetzt werden - gesucht wird dann nach dieser Folge von Zeichen:
|
Dieses Skript liefert nur in dem ersten Test "wahr
",
ansonsten immer "falsch
", da der reguläre
Ausdruck "ede
" genau
passen muß. D.h., es wird hier auf Groß- und Kleinschreibung
geachtet
(Test 2) und der Ausdruck muß so wie er definiert ist in der
Zeichenkette auftreten - es genügt nicht, daß die Buchstaben
"e
", "d
" und "e
"
irgendwo im durchsuchten String stehen (Test 3).
Zu beachten ist, daß nicht nach jedem Zeichen einfach gesucht werden kann wie im obigen Beispiel, da einige in regulären Ausdrücken besondere Bedeutung haben. Es handelt sich dabei um
. ? * + ^ $ | \ ( ) [ {
sowie um das Zeichen, das als "Klammer" um den regulären
Ausdruck verwendet wird (meistens der Schrägstrich
"/
"). Will man nach diesen Symbolen suchen, so
muß man einen Backslash ("\
") voranstellen:
|
Diese Tests liefern alle "wahr
". Wie man beim Vergleich von
Test 2 und Test 3 sieht, ist es beim Testen von
UNIX-Pfadnamen oft sinnvoll, die Schrägstrich-Klammerung zu ersetzen, um
allzuviele Backslashes zu vermeiden.
Man beachte im Test 4 noch die einfachen Anführungsstriche, die
verhindern, daß im String aus \$
ein einfaches $
wird.
Für einige häufig gebrauchte Zeichen, die sich schlecht direkt darstellen lassen, gibt es besondere Symbole (siehe auch unter Stringkonstanten):
Zeichen Bedeutung \n
neue Zeile \r
Wagenrücklauf \f
Seitenvorschub \t
Tabulator \a
Signalton \e
Escape
Außerdem lassen sich auch alle Zeichen durch ihren ASCII-Code (in oktaler
Darstellung) schreiben, indem man dem 3-stelligen Code einen Backslash voranstellt
(so ist beispielweise "\101
" äquivalent zum Buchstaben
"A
"). Anmerkung: es müssen immer drei Ziffern
sein - also notfalls vorne mit einer oder zwei Nullen auffüllen.
Eine ganz besondere Bedeutung hat in regulären Ausdrücken der Punkt
".
": er steht normalerweise für jedes beliebige
Zeichen mit Ausnahme des Zeilenvorschubs "\n
".
|
Test 1 ergibt "wahr
", da die Suche in der Mitte von
$t
erfolgreich ist. Test 2 dagegen liefert
"falsch
" (keine zwei aufeinanderfolgenen Tabulatoren
in $t
).
Test 3 wiederum verläuft erfolgreich - er findet einen
zum regulären Ausdruck passenden Substring
(1.Spalte\t2.Spalte
).
Bisweilen möchte man nicht nur nach einem bestimmten Zeichen suchen
(beispielsweise dem Buchstaben "a"), sondern nach einem
Zeichen, das einer Gruppe angehört (z.B. nach einem "Vokal", d.h.,
nach einem Buchstaben aus der Menge
{a
,e
,i
,o
,u
}).
Eine solche Klasse kann mit Hilfe der eckigen Klammern
("[...]
")
definiert werden. Damit wird dann im Vorgabestring nach einem Symbol aus dieser
Klasse gesucht.
|
Hier liefert nur Test 1 "wahr
"
("o
" aus der Klasse "[aeiou]
"
in "Computer
" gefunden);
die zweite Suche aber schlägt fehl (weder "x
" noch
"y
" noch "z
" kommen in
"Computer
" vor).
Auch in Zeichenklassen muß (wie bei einfachen Symbolen in regulären Ausdrücken) darauf geachtet werden, daß einige Zeichen eine besondere Bedeutung haben:
^ - ] \ $
Benötigt man eines dieser Zeichen explizit innerhalb einer Zeichenklasse, so muß man einen Backslash voranstellen.
Hier sei speziell die Wirkung von Zirkumflex ("^
")
und Minuszeichen ("-
") für Zeichenklassen beschrieben.
Ersterer bedeutet (wenn er unmittelbar
nach der öffnenden Klammer ("[
") steht) soviel wie
"alle Zeichen außer ...". Das Minus wird verwendet, um eine Reihe
aufeinanderfolgender (im Sinne des verwendeten Zeichensatzes) Symbole
abkürzend darzustellen
(z.B. alle Kleinbuchstaben durch "[a-z]
").
|
Die Ausgabe ist jeweils "wahr
". Im ersten Test wird die Zahl
1
als Mitglied der Klasse der Ziffern 0-9
erkannt.
Test 2 ist erfolgreich beim ersten Auftreten von doppelten
Anführungszeichen vor der 1
(die einfachen
Anführungszeichen
in der Defintion $t = ...
gehören ja nicht zu
$t
). Im dritten Test schließlich wird das
"s
" in "Ist
"
gefunden, da es das erste Zeichen ist, das nicht zur Klasse der
Großbuchstaben gehört.
Für einige oft verwendete Zeichenklassen gibt es abkürzende Schreibweisen:
Zeichen Entsprechung Bedeutung \d
[0-9]
Ziffer \D
[^0-9]
Gegenstück zu \d
\w
[a-zA-Z_0-9]
Buchstabe, Unterstrich oder Ziffer \W
[^a-zA-Z_0-9]
Gegenstück zu \w
\s
[ \t\n\f\r]
Leerzeichen \S
[^ \t\n\f\r]
Gegenstück zu \s
(zur Erinnerung: \t
, \n
, \f
und
\r
stehen für Tabulator, neue Zeile,
Seitenvorschub und Wagenrücklauf.)
Ein Sonderfall ist das Zeichen "\b
", das innerhalb
einer Klasse für einen Rückschritt (backspace) steht.
Außerhalb einer Zeichenklasse besitzt "\b
" aber
eine völlig andere Bedeutung, wie wir später sehen werden.
Diese Abkürzungen können wiederum in Klassen verwendet werden.
|
Die Klasse "[-\d.]
" veranlaßt die Suche nach
dem ersten
Auftreten eines der Symbole aus der Menge {-
,0
,1
,2
,3
,4
,5
,6
,7
,8
,9
,.
} (hier
wird das Minuszeichen zuerst gefunden).
Anmerkungen: Das
Minuszeichen besitzt hier keine Sonderbedeutung (wie in
"[a-z]
"), da es am Anfang der Klasse steht. Beim Punkt
kann in Zeichenklassen auf das Voranstellen eines Backslashes verzichtet werden.
Selbstverständlich können einfache Zeichensuche und Zeichenklassen in regulären Ausdrücken miteinander kombiniert werden. Beispiel: Suche nach einem Teilstring, der eine Versionsnummer beschreibt.
|
Besonders flexibel werden reguläre Ausdrücke durch die Möglichkeit nach dem mehrfachen Auftreten von Zeichen zu suchen (wobei "mehrfach" auch "keinmal" einschließt).
Dies erfolgt im allgemeinsten Falle durch die Angabe eines Zahlenbereichs in
geschweiften Klammern. So bedeutet beispielsweise "{2,5}
",
daß das vorangehende Symbol 2, 3, 4 oder 5-mal auftreten darf.
Ein Beispiel:
|
Test 1 gibt den Wert "wahr
" aus. Hier ist das
gefundene Muster (in der Variablen $&
) von besonderem
Interesse: es werden
drei Minuszeichen ausgegeben. Darin erkennt man eine wichtige Eigenschaft
der Suche nach mehrfachen Zeichen: Perl versucht, möglichst viele davon
zu finden. Dies sehen wir auch im zweiten Test: hier wird die Maximalzahl von
vier Minuszeichen als gefundener Substring ausgegeben. Dagegen liefert der
Test 3 den Wert "falsch
", da die Bedingung nicht einmal
durch die kleinste Zahl (5) erfüllt werden kann.
Da solche Mehrfachsuchen sehr häufig in Perl benutzt werden, gibt es einige Abkürzungen, die die Schreibarbeit vereinfachen (und auch zur Übersichtlichkeit beitragen):
Abkürzung Entsprechung Bedeutung {
n}
{
n,
n}
genau n-mal {
n,}
mindestens n-mal ?
{
0,
1}
höchstens einmal +
{
1,}
mindestens einmal *
{
0,}
beliebig oft
Beispiele:
|
Die Variable $t
besteht alle Tests erfolgreich; die folgende
Liste zeigt, an welchen Stellen jeweils der reguläre Ausdruck paßt
(Perl sucht hier immer die größtmögliche Lösung):
xxxAzz
(genau zwei aufeinanderfolgende "x
") xxxAzz
(mindestens zwei aufeinanderfolgende "x
") xxxAzz
(mindestens ein "z
") xxxAzz
("B
" wird 0-mal "gefunden" - daher auch hier "wahr
") xxxAzz
("x
" gefolgt von "A
" evt. gefolgt von "y
") xxxAzz
(beliebig viele "x
" gefolgt von mindestens einem "z
")
Wie man in Test 5 sieht, bezieht sich ein Sonderzeichen
zur Mehrfachsuche nur auf das direkt davor stehende Zeichen (im Beispiel
also nur auf das "y
"), nicht aber auf andere
Symbole weiter vorne.
Wenn gesagt wird, daß Perl die größtmögliche Lösung sucht, so ist dies eigentlich nicht ganz richtig; man sollte genauer sagen: die längste Lösung, die sich bei der Suche ab dem Startpunkt des vorgegebenen Strings ergibt. Eine Suche beginnt immer vor dem ersten Zeichen der zu durchsuchenden Zeichenkette. Wird, von dieser Startposition ausgehend, eine Lösung gefunden, so wird die Suche (erfolgreich) abgebrochen, auch wenn es vielleicht weiter hinten noch eine längere Lösung gäbe. Bei einem Fehlschlag beginnt eine erneute Suche ein Zeichen weiter hinten; dieses Verfahren wiederholt sich so lange, bis eine Lösung gefunden oder das Ende des Strings erreicht wird.
|
In Test 1 bietet Perl als gefundenen Substring "aa
"
("aa==aaaa
") an, obwohl die Lösung
"aaaa
" ("aa==aaaa
")
länger wäre.
Ein scheinbar paradoxes Verhalten zeigt Test 2: hier ist die Lösung
leer! Die Erklärung dafür ist, daß von der
Startposition ausgehend eine Lösung gefunden wurde
(0-mal "=
") und dann die Suche abgebrochen wird.
Test 3 schließlich gibt "==
" aus, da
ja hier mindestens ein "=
" gefunden werden muß.
Die Problematik, daß Perl immer versucht, eine möglichst große Lösung zu finden, wurde schon angesprochen. Dies ist bisweilen unerwünscht; daher gibt es seit Perl 5 auch eine Variante, um eine möglichst kurze Lösung zu suchen. Dies geschieht, indem einfach ein Fragezeichen angehängt wird. Die entsprechenden Kombinationen sehen dann so aus:
Quantifizierer Bedeutung {
n,
m}?
mindestens n-mal, höchstens m-mal {
n}?
genau n-mal (äquivalent zu {
n}
){
n,}?
mindestens n-mal ??
höchstens einmal +?
mindestens einmal *?
beliebig oft
Ein Beispiel, das den Unterschied zwischen minimaler und maximaler Suche verdeutlicht:
|
Der erste Test gibt "mmmmm
" als (maximale) Lösung aus,
während
Test 2 nur "m
" (die minimale Lösung) liefert.
Der dritte Test schließlich bietet als Lösung gar einen
leeren String an (0-mal "m
").
Es sei hier noch erwähnt, daß beispielweise "m*?
"
keinesfalls immer automatisch null Zeichen bedeuten muß wie man nach dem
obigen Beispiel vielleicht zunächst glauben mag.
|
Hier enthält der gefundene Substring im Test 1 drei
"m
" ("AmmmAmmmA
"). Lehrreich
ist auch der Vergleich der beiden anderen Tests: maximale Lösung in
Test 2 ("AmmmAmmmA
") und minimale
Lösung in Test 3 ("AmmmAmmmA
").
Im Abschnitt zur Mehrfachsuche wirkte eine entsprechende Anweisung immer
nur auf das eine unmittelbar davor stehende Zeichen. Mit Hilfe von
Klammern ("(...)
") können auch längere
Ausdrücke mehrfach gesucht werden.
Dazu ein Beispiel:
|
Test 1 liefert hier "falsch
" (es wird nach einer
"0
" gefolgt von 2 oder 3 "3
"en
gesucht). Erfolgreich dagegen verläuft Test 2: er findet
"030303
" (längste Lösung).
Klammern bewirken auch noch einen Nebeneffekt: Derjenige Substring, der auf
das Muster der ersten Klammer paßt, wird in die Variable $1
geschrieben, der zweite Substring in $2
, usw. Zur Festlegung der
Reihenfolge der Klammern zählt die jeweils öffnende Klammer (dies
ist wichtig bei ineinander verschachtelten Klammerpaaren).
|
Auch dieser recht kompliziert aussehende reguläre Ausdruck läßt sich einfach auflösen:
(\w+)
" sucht nach mindestens einem
alphanumerischen Zeichen und speichert ggf. den gefundenen Substring
in der Variablen $1
.
([\d.]+ m)
" sucht nach mindestens einem
Zeichen aus der Gruppe "Ziffern und Punkt" gefolgt von einem
Leerzeichen und dem Buchstaben "m
"; das Ergebnis
landet ggf. in $2
.
Somit enthält schließlich $1
den Wert
"Breite
" und $2
den Wert
"7.5 m
". Da alle Bedingungen der
Suche erfüllt sind, ergibt sich natürlich der Gesamtwert
"wahr
".
Möchte man den Nebeneffekt der Zuweisung von $1
,
$2
,
$3
,... verhindern, fügt man nach der öffnenden Klammer
"?:
" ein.
|
Im ersten Test werden sowohl $1
(mit "a
") als auch
$2
(mit "b
") belegt. Im zweiten Test dagegen findet
bei der ersten Klammer keine Zuweisung statt und der Inhalt der zweiten
Klammer ("b
") wird in $1
geschrieben.
Es ist sogar möglich, schon innerhalb des regulären Ausdrucks auf
vorher im String gefundene Teilstrings zuzugreifen. So findet sich der
Wert der ersten Klammer in \1
, der der zweiten Klammer in
\2
, usw. Die Zuordnung ist identisch zu der von $1
,
$2
, usw., allerdings dürfen die "Backslash-Variablen"
nur innerhalb des Suchmusters und die "Dollar-Variablen" nur
außerhalb verwendet werden.
|
Der reguläre Ausdruck sucht nach genau zwei gleichen Zeichen, die aber
ansonsten beliebig gewählt sein können, gefolgt von einer möglichst
kurzen Zeichenkette aus mindestens einem Symbol gefolgt von dem Zeichenpaar,
das in der ersten Klammer gefunden wurde. Der gefundene Substring lautet
dann "--defg-h--
" und $1
enthält
wie erwartet "--
".
Die Verwendung von \1
, \2
, usw. kann zu Problemen
führen, wenn sich daran eine Suche nach Ziffern anschließt; eine
mögliche Lösung ist die Benutzung von Klammern des Typs
"(?:...)
":
|
Hier schlägt Test 1 fehl, da "\112
" als
Oktal-Code interpretiert wird (Zeichen "J
"). Die
zusätzliche Klammer
in Test 2 sorgt für die korrekte Lösung
("--456--12
").
Wie man bei der Suche nach einzelnen Zeichen eine Auswahl vorgeben kann, haben
wir im Abschnitt zu den Zeichenklassen gesehen. Natürlich möchte man
diese Möglichkeit auch bei längeren Ausdrücken zur Verfügung
haben. Dazu wird der senkrechte Strich ("|
") verwendet, der die
einzelnen Alternativen voneinander trennt.
|
Bei der Suche werden die Alternativen von links beginnend durchprobiert bis
ein passendes Muster gefunden wurde (hier: "Perl
").
Bei komplexeren Ausdrücken müssen meist Klammern gesetzt werden, damit klar ist, welche Teile zu den Alternativen gehören und welche nicht.
|
Beide Tests sind zwar erfolgreich, liefern aber unterschiedliche Ergebnisse
in $&
("97
" bzw.
"1997
")
Ankerpunkte bieten die Möglichkeit festzulegen, daß der gesuchte Substring an einer bestimmten Stelle in der vorgegebenen Zeichenkette auftreten muß.
Soll nach einem Muster nur am Beginn eines Strings gesucht werden, so setze
man einen Zirkumflex ("^
") oder
"\A
" an den Beginn des regulären Ausdrucks.
Solange die untersuchte Zeichenkette keinen Zeilenvorschub enthält
verhalten sich die beiden Ankerpunkte identisch (mehr hierzu im Abschnitt
zu den Optionen von regulären Ausdrücken).
Analog erfolgt die Suche am Ende eines Strings: hier sind die Symbole, die am
Ende des regulären Ausdrucks stehen, das Dollar-Zeichen
("$
") bzw. "\Z
". Auch diese
Ankerpunkte unterscheiden sich nicht in ihrer Wirkung solange keine
Zeilenvorschübe vorhanden sind.
|
Test 1 findet den passenden Substring am Anfang
von $t
(abrakadabra
),
während Test 2
am Ende fündig wird (abrakadabra
).
Die Suche in Test 3 dagegen bleibt erfolglos; es ist zwar ein
Substring "kada
" vorhanden, der steht aber nicht wie gefordert am Anfang von
$t
. Auch Test 4 liefert "falsch
"; auch wenn
"abra
" sowohl am Anfang als auch am Ende von $
t steht, so
sind dies doch zwei verschiedene Substrings.
Man beachte, daß diese Ankerpunkte gewissermaßen eine höhere
Priorität besitzen als beispielsweise das Alternativ-Symbol ("|
"):
|
Test 1 liefert den Wert "wahr
", da er "Computer
" am
Ende von $t
findet ("Suche nach 'Rechner
' am Anfang oder 'Computer
'
am Ende"). Test 2 dagegen gibt die Antwort "falsch
",
da hier der Suchauftrag lautet: "Suche nach 'Rechner
' oder 'Computer
', die
sich von Anfang bis Ende des Strings erstrecken".
Weitere Ankerpunkte können Wortgrenzen sein. Dabei ist eine solche Grenze
definiert als der Punkt zwischen einem Zeichen aus der Klasse \w
und einem aus \W
. Eine solche Wortgrenze wird durch "\b
"
dargestellt (dabei handelt es sich nicht um ein Zeichen an sich, sondern den Raum
zwischen zwei Symbolen!). Das Gegenstück zu "\b
" ist
"\B
" (Ankerpunkt inner- oder außerhalb eines Wortes).
|
Im Test 1 wird nach dem ersten Großbuchstaben an einer
Wortgrenze gesucht; hier wird das "D" ("Der ASCII-Code")
gefunden, auch wenn es hier nicht (am Anfang des Strings) nach einem Zeichen
aus \W
steht,
da Anfang und Ende eine entsprechende Sonderbehandlung erfahren. Test 2
dagegen sucht nach einem Großbuchstaben innerhalb eines Wortes; die
Lösung lautet hier "S" ("Der ASCII-Code").
Insbesondere bei der vorausschauenden und zurückblickenden Suche gab es im Laufe der einzelnen Perl-5-Versionen einige Erweiterungen.
Man kann damit den Erfolg einer Mustersuche davon abhängig machen, ob nicht nur das gesuchte Muster selbst paßt, sondern auch die Umgebung, d.h. die Zeichen davor und dahinter, mit berücksichtigen. So kann man etwa sagen: "Suche den Teilstring 'Regen', aber nur, wenn er nicht von 'wurm' gefolgt wird."
Folgende Klammerausdrücke stehen zur Verfügung:
Ausdruck Bedeutung Erläuterung (?=...)
positive Vorausschau nur dann erfolgreich, wenn ... folgt (?!...)
negative Vorausschau nur dann erfolgreich, wenn ... nicht folgt (?<=...)
positive Rückschau nur dann erfolgreich, wenn ... vorangeht (?<!...)
negative Rückschau nur dann erfolgreich, wenn ... nicht vorangeht
|
|
Hierbei ist unbedingt zu beachten, daß der Teilstring, der zu den
Ausdrücken in einer der Klammern gehört, nicht Bestandteil
der Lösungsvariablen $&
ist (er gehört nicht
zum gefundenen
Muster, sondern stellt nur eine Bedingung an die Umgebung dar). Im obigen
Beispiel werden die vollständigen Wörter nur deswegen
ausgegeben, weil die jeweiligen Worthälften auf das zusätzliche Muster
"\w+
" passen.
Um bei komplexen regulären Ausdrücken die Übersicht zu
behalten, können Kommentare in Klammern wie diesen
"(?#...)
" eingefügt werden.
|
Man beachte, daß hier "\2
" verwendet werden muß,
um das Zeichen, das auf ".
" paßt, zu verwenden, da der
Punkt nach der zweiten öffnenden Klammer steht.
Autor: Eike Grote | Version: 2.07 (27.10.2019) |