Was bedeutet $ wirklich?
Inhalt:
Dies ist ein Template für den FAQ-Unterbereich
RegulaereAusdrueckeEndOfString.
Einleitung
Reguläre Ausdrücke in traditionellen Anwendungen (grep, sed, vi, ...) werden meist auf Zeilen angewendet. Daher gibt es traditionell kaum einen Unterschied zwischen "Ende der Zeile" und "Ende des Strings", und die Newlines zwischen den Zeilen stehen dabei außerhalb des Wirkungsbereichs der regulären Ausdrücke.
In Perl ist das alles etwas komplizierter. Viele sind sich darüber jedoch nicht im Klaren. Daher kommt auch die weitverbreitete Annahme, dass $ in einem regulären Ausdruck auf das Ende des Strings matcht.
$ in Perl
In Wirklichkeit matcht $ in Perl in zwei Fällen:
- Auf das Ende des Strings
- Auf die Stelle (also den leeren String) direkt vor einem Newline, das am Ende des Strings steht
Man kann sich dies in einem Beispiel deutlich machen:
"hallo\n" =~ m/($)\n$/ and print "Match!";
Hier matcht $ zwei Mal in demselben String an unterschiedlichen Stellen: Einmal vor dem Newline und einmal danach. Um das alles noch komplizierter zu machen, sind die runden Klammern notwendig, damit $\ nicht als Variable geparst wird.
Ein anderes lustiges Beispiel:
print "Match!\n" while "\n\n\n\n\n\n\n\n" =~ m/$/g;
Hier wird "$" in einem String aus acht Newlines gesucht, und es gibt genau zwei Matches, naemlich eines vor dem letzten der acht Newlines, und eines danach. Daher sieht man in der Ausgabe zwei Mal "Match!".
Meiner (betterworlds) Meinung nach ist dies ein ziemlich dummes "Feature" von Perl, denn es gibt weitaus weniger Fälle, wo es wirklich nützlich ist, als es Fälle gibt, wo Leute sich nicht darüber im Klaren sind, was ihr Regex wirklich tut.
OK, aber ich will wirklich nur das Ende des Strings
Mit \z kann man das Ende des Strings matchen, und nur das Ende des Strings. \z tut das, was $ nach der Vorstellung einiger Anfänger und Fortgeschrittenen tut. Achtung: \Z (großes Z) tut dasselbe wie $ (jedenfalls ohne /m).
Der Anfang des Strings
^ kann benutzt werden, um den Anfang des Strings zu matchen. Hierbei gibt es keine Probleme mit Newlines. Wenn jedoch /m aktiv ist (siehe unten), muss man \A verwenden, um den Anfang zu matchen. Wenn man einheitlich sein möchte, kann man \A auch ohne /m verwenden; in dem Fall tut es dasselbe wie ^.
Sicherheitsaspekt
Die Kenntnis über den Unterschied zwischen $ und \z kann essentiell für die Sicherheit einer Perl-Anwendung sein. Oft werden reguläre Ausdrücke zur Validierung von Benutzereingaben benutzt. Wenn ich zum Beispiel einen neuen Benutzer anlegen möchte und ihn in /etc/passwd eintragen möchte, sollte ich sicherstellen, dass er keine Sonderzeichen enthält, die in dieser Datei stören würden:
$username =~ /^[a-z]+\z/ or die "Illegal user name.\n";
Wenn ich hier $ statt \z benutzt hätte, würde man mein Script dazu bringen können, ein Newline in /etc/passwd einzufügen, womit die Benutzerdatenbank ziemlich korrupiert wäre.
Das Ende der Zeile
Um es noch komplizierter zu machen, kann man die Bedeutung von $ mit dem Flag /m verändern. Wenn dieses Flag angewandt wird, matcht $ in den folgenden Fällen:
- Auf das Ende des Strings
- Auf die Stelle (also den leeren String) direkt vor einem Newline (egal wo es steht)
Auch dies kann man sich mit einem Beispiel vertraut machen:
"hallo\nwelt\n" =~ m/hallo($)\nwelt($)\n$/m and print "Treffer!";
Hierzu beachte man
nicht perldoc perlre (aus 5.8.8):
m Treat string as multiple lines. That is, change "^" and "$" from
matching the start or end of the string to matching the start or
end of any line anywhere within the string.
(Und weiter unten:)
You may, however, wish to treat a string as a
multi-line buffer, such that the "^" will match after any newline
within the string, and "$" will match before any newline.
Dies ist irreführend. Erstens ist "end of the string" nicht korrekt (siehe oben), zweitens ist "before any newline" nicht ausreichend, da zusätzlich wie beschrieben auch noch das Ende des Strings gematcht wird.
ChristophBussenius - 12. 4. 2008