Der »Pattern«-Parameter
(Auszug aus "Reguläre Ausdrücke" von Jeffrey E. F. Friedl)
Der erste Parameter bei jeder preg-Funktion ist das Pattern, dieser besteht aus der Regex, umrahmt von Begrenzungszeichen, und optional gefolgt von Modifikatoren. Im ersten Beispiel in Die preg-Programmierschnittstelle ist das Pattern '/<table\b/i'; das heißt, die eigentliche Regex ist ˹<table\b˼, es werden Schrägstriche als Begrenzer verwendet und der i-Modifikator (Groß- und Kleinschreibung ignorieren) ist gesetzt.
Strings in Hochkommas in PHP
Weil reguläre Ausdrücke selbst sehr oft Backslashes enthalten, ist es in den meisten Fällen von Vorteil, das Pattern-Argument als Hochkomma-String zu schreiben. Die verschiedenen Typen von String-Literalen in PHP wurden schon unter Features und Dialekte (siehe Strings in PHP) beschrieben: Bei Strings in Hochkommas kann man in fast allen Fällen auf das Verdoppeln der Backslashes verzichten. Die Hochkomma-Strings in PHP kennen nur die zwei Metasequenzen für das Hochkomma selbst und für den Backslash, ›\'‹ und ›\\‹.
Eine größere Anzahl von Backslashes benötigt man eigentlich nur, wenn mit ˹\\˼ in der Regex nach einem Backslash gesucht wird. Dafür muss jedes ˹\˼ mit einem Backslash maskiert werden, man muss also für einen einzelnen Backslash \\\\ schreiben. Uff!
(Das Programmbeispiel, siehe Die Lösung, ist von Backslashitis dieser Art infiziert.)
Als konkretes Beispiel betrachten wir eine Regex, die den »Laufwerksbuchstabenteil« eines Windows-Pfadnamens erkennen soll, also einen String wie ›C:\‹. Eine Regex dafür ist ˹^[A-Z]:\\$˼, aber in einem Hochkomma-String muss man dies so schreiben: '^[A-Z]:\\\\$'.
In der unteren Tabelle in Verzeichnisangabe aus Pfadnamen entfernen in Regex-Methoden aus der Praxis verwendeten wir dafür ein Pattern-Argument mit drei Backslashes. Die folgenden Beispiele sind für das Verständnis ganz nützlich:
print '/^.*\/'; Gibt aus: /^.*\/
print '/^.*\\/'; Gibt aus: /^.*\/
print '/^.*\\\/'; Gibt aus: /^.*\\/
print '/^.*\\\\/'; Gibt aus: /^.*\\/
Die ersten zwei Versionen ergeben das gleiche Resultat, aber auf verschiedenen Wegen. Die Sequenz ›\/‹ im ersten Beispiel hat für Hochkomma-Strings keine besondere Bedeutung, die zwei Zeichen erscheinen also unverändert im Wert des Strings. Im zweiten Beispiel ist ›\\‹ eine Metasequenz für einen einzelnen Backslash, das ergibt ›\‹ im Wert des Strings. Darauf folgt der normale Schrägstrich wie im ersten Beispiel. Das dritte und vierte Beispiel verhalten sich ganz analog.
Man kann natürlich auch in PHP die Strings in Anführungszeichen (»Gänsefüßchen«) verwenden, aber diese sind in den meisten Fällen deutlich unpraktischer. Die Strings in Anführungszeichen haben eine ganze Anzahl von eigenen Metasequenzen, die man alle maskieren muss, wenn man eine Regex-Metasequenz benötigt.
Begrenzungszeichen
Bei den preg-Funktionen muss der Regex-String in Begrenzer eingefasst sein, weil sich die PHP-Entwickler nah an der Perl-Syntax orientierten, gerade bei der Art, wie man Pattern-Modifikatoren notiert. Andere halten diese Design-Entscheidung für falsch, weil man die Modifikatoren ja auch auf andere Weise angeben und auf die Begrenzer verzichten könnte (die Begrenzer können auch zu Problemen führen, siehe den Kasten Der Fehler »Unknown Modifier«).
Meistens nimmt man für die Begrenzer Schrägstriche, aber man kann jedes nicht-alphanumerische ASCII-Zeichen außer Whitespace und Backslash verwenden. Oft sieht man ›!‹ und ›#‹.
Wenn der erste Begrenzer ein »öffnendes« Zeichen ist:
{ ( < [
dann muss als zweiter Begrenzer das entsprechende schließende Zeichen verwendet werden:
} ) > ]
Wenn solche gepaarte Begrenzer verwendet werden, dürfen sie auch verschachtelt sein, man kann also so etwas wie '((\d+))' als Pattern-Argument angeben. Dabei sind die äußeren Klammern die Begrenzer und nur die innere Klammer ist Teil der Regex. Das verwirrt aber mehr, als es nützt; in diesem Fall würde ich mich nicht darauf verlassen und etwas Einfacheres wie '/(\d+)/' verwenden.
Wenn das Begrenzungszeichen in der Regex selbst vorkommt, muss es mit einem Backslash maskiert werden. So etwas wie '/<B>(.*?)<\/B>/i' ist also zulässig, aber eine Version wie '!<B>(.*?)</B>!i' mit ›!...!‹ oder '{<B>(.*?)</B>}i' mit ›{...}‹ als Begrenzungszeichen ist sicher besser lesbar.
Pattern-Modifikatoren
Nach dem schließenden Begrenzungszeichen kann man eine Reihe von Modifikatoren (im Handbuch Suchmuster-Modifikatoren genannt) angeben; manche davon auch innerhalb der Regex selbst. Der Modifikator i zum Ignorieren von Groß- und Kleinschreibung ist uns schon begegnet. Hier eine Liste der unterstützten Modifikatoren:
Tabelle: Pattern-Modifikatoren in PHP.
Modifikator | Inline | Beschreibung | |
---|---|---|---|
i | ˹(?i)˼ | siehe Der Modus »Groß- und Kleinschreibung ignorieren« | Groß- und Kleinschreibung ignorieren |
m | ˹(?m)˼ | siehe Der Modus »Verbesserte Zeilenanker« oder Mehrzeilenmodus | Mehrzeilenmodus oder Modus »verbesserte Zeilenanker« |
s | ˹(?s)˼ | siehe Der Modus »Punkt passt auf alles« | »Punkt passt auf alles« |
x | ˹(?x)˼ | siehe Der Modus »Freie Form« | Modus »Freie Form« |
u | siehe PHP-spezifische Modifikatoren | Regex und Suchstring sind in UTF-8 codiert | |
X | ˹(?X)˼ | siehe PHP-spezifische Modifikatoren | Die »Extra«-Optionen von PCRE einschalten |
e | siehe Einfaches preg_replace mit je einem Pattern, Ersatztext und Suchstring | Ersatzstring soll als PHP-Code ausgeführt werden (nur preg_replace) | |
S | siehe unter Der S-Modifikator | Die »Study«-Optimierung von PCRE verwenden | |
Die folgenden Modifikatoren werden nur selten verwendet: | |||
U | ˹(?U)˼ | siehe PHP-spezifische Modifikatoren | Bedeutung von ˹*˼ und ˹*?˼ umkehren |
A | siehe PHP-spezifische Modifikatoren | Treffer muss an der aktuellen Suchposition beginnen | |
D | siehe PHP-spezifische Modifikatoren | ˹$˼ passt nur am Ende des Strings, nicht auf das Newline davor (keine Wirkung, wenn der m-Modifikator verwendet wird) |
Modifikatoren innerhalb der Regex
Die Pattern-Modifikatoren können auch »inline«, also innerhalb der Regex, ein- und ausgeschaltet werden. Beispielsweise schaltet ˹(?i)˼ die Option »Groß- und Kleinschreibung ignorieren« ein und ˹(?-i)˼ schaltet sie wieder aus (siehe Modus-Modifikatoren).
Dabei wirken die Modifikatoren nur bis zum Ende eines Klammerausdrucks, in dem sie vorkommen, oder wenn es kein solches Klammerpaar gibt, bis zum Ende der Regex.
Sie können aber auch als Modifikator-Bereiche benutzt werden (siehe Modus-Modifikatoren mit Klammerung). Zum Beispiel schaltet ˹(?i:...)˼ den i-Modifikator nur für diesen Bereich ein, ˹(?-sm:...)˼ schaltet die Modifikatoren s und m für den angegebenen Bereich aus.
Modifikatoren außerhalb der Regex
Die Modifikatoren können in beliebiger Kombination und Reihenfolge nach dem schließenden Begrenzungszeichen angegeben werden, in diesem Beispiel werden mit »si« die Optionen »Groß- und Kleinschreibung ignorieren« und ›Punkt passt auf alles‹ eingeschaltet:
if (preg_match('{<title>(.*?)</title>}si', $html, $treffer))
PHP-spezifische Modifikatoren
Die ersten vier Modifikatoren in der obigen Tabelle gibt es in fast jeder modernen Implementation; sie werden unter Features und Dialekte (siehe Der Modus »Groß- und Kleinschreibung ignorieren«) behandelt. Der e-Modifikator hat nur bei preg_replace eine Wirkung und wird im entsprechenden Abschnitt behandelt (siehe Einfaches preg_replace mit je einem Pattern, Ersatztext und Suchstring).
Der Modifikator u bewirkt, dass sowohl Regex als auch Suchstring als UTF-8-codiert betrachtet werden. Der Modifikator verändert die Bytes nicht, er bewirkt nur, dass sie von der Regex-Maschine unterschiedlich interpretiert werden. Üblicherweise (also ohne den u-Modifikator) werden Suchstring und Regex als 8-Bit-Strings im eingestellten Locale betrachtet (siehe unter POSIX – ein Standardisierungsversuch). Man soll den Modifikator also nur verwenden, wenn bekannt ist, dass die Daten in UTF-8 vorliegen. Zeichen außerhalb des ASCII-Bereichs werden in UTF-8 als Folge von mehreren Bytes dargestellt, der u-Modifikator bewirkt, dass eine solche Bytefolge als ein einziges Zeichen betrachtet wird.
Mit dem X-Modifikator wird zusätzliche PCRE-Funktionalität aktiviert. Dies bewirkt im Moment nur, dass ein Fehler erzeugt wird, wenn in einer Regex ein Backslash auftritt, der nicht Teil eines bekannten Metazeichens ist. Zum Beispiel hat die Sequenz ˹\k˼ für die Regex-Maschine von PCRE keine besondere Bedeutung und wird normalerweise einfach als ˹k˼ behandelt (der Backslash, der nicht Teil einer bekannten Metasequenz ist, wird ignoriert). Mit aktiviertem X-Modifikator wird eine Fehlermeldung ausgegeben (»unrecognized character follows \«) und das Programm bricht ab.
Es ist denkbar, dass Backslash-Buchstaben-Kombinationen, die in den aktuellen Versionen von PHP und PCRE keine bekannten Metasequenzen sind, in zukünftigen Versionen eine bestimmte Bedeutung bekommen. Es ist daher (und auch aus Gründen der Lesbarkeit) nur vernünftig, Backslashes nur dort zu verwenden, wo sie notwendig sind. In dieser Hinsicht ist der X-Modifikator sehr hilfreich; er kann auf Tipp- und andere Fehler hinweisen.
Mit dem S-Modifikator wird die »study«-Option von PCRE eingeschaltet. Die Regex wird dann vor dem Matching auf Optimierungsmöglichkeiten untersucht; das kann in bestimmten, klar definierten Fällen einen enormen Laufzeitgewinn bringen. Die Option wird im Abschnitt über Effizienz bei Der S-Modifikator behandelt.
Die weiteren Modifikatoren sind eher abgehoben und werden nur selten verwendet:
- Mit dem A-Modifikator wird erzwungen, dass ein Treffer an der Stelle beginnt, an der die Treffersuche beginnt; als ob die Regex mit ˹\G˼ beginnen würde. In der Analogie mit dem Getriebe von Wie Regex-Maschinen arbeiten bedeutet das, dass das »Weiterschalten« des Getriebes deaktiviert wird (siehe unter Das »Getriebe« schaltet zum nächsten Zeichen).
- Mit dem D-Modifikator wird ˹$˼ wie ein ˹\z˼ behandelt (siehe bei Der Modus »Verbesserte Zeilenanker« oder Mehrzeilenmodus), also passt ˹$˼ nur noch auf das Ende des Suchstrings, aber nicht mehr auf ein Newline direkt davor.
- Mit dem U-Modifikator wird die Wirkung der gierigen und nicht-gierigen Quantoren vertauscht: ˹*˼ wird zu ˹*?˼ und umgekehrt, ˹+˼ wird zu ˹+?˼ und umgekehrt usw. Nach meiner Auffassung ist der primäre Zweck dieses Modifikators der, Verwirrung zu stiften, ich rate deswegen von seiner Benutzung unbedingt ab.
Der Fehler »Unknown Modifier«Mehr als einmal ist es mir passiert, dass ein Programm während der Entwicklung plötzlich die Fehlermeldung »Unknown Modifier« ausgegeben hat. Es hat sich dann herausgestellt, dass ich nur vergessen habe, beim Pattern-Parameter die Begrenzungszeichen anzugeben. Zum Beispiel wollte ich auf ein HTML-Tag testen:
Die spitze Klammer am Anfang sollte eigentlich auf den Anfang des Tags passen, aber für preg_match – und wer wollte es ihm verübeln – ist das der öffnende Teil eines Begrenzungszeichen-Paars. Der Rest des Parameters wird folgerichtig so eingelesen, dass der graue Teil in ›<(\w+)([^>]*)>‹ als Regex und der unterstrichene als Modifikator interpretiert wird. Natürlich hat die Regex ˹(\w+)([^˼ einen Syntaxfehler, aber so weit kommt es gar nicht. Bevor die Regex-Maschine angeworfen wird, werden die Modifikatoren ausgewertet, und da keines der Zeichen in ›]*)>‹ ein gültiger Modifikator ist, wird schon beim ersten die Fehlermeldung ausgegeben:
Hinterher ist man immer schlauer und weiß »natürlich«, dass Begrenzungszeichen benötigt werden:
Das Problem ist, dass es außer bei regulären Ausdrücken noch eine Menge von anderen »Modifiern« gibt, und wenn ich nicht gerade an die Pattern-Modifikatoren von PHP denke, kann es schon einige Zeit dauern, bis ich die Fehlermeldung richtig einordnen kann. Ich komme mir dann recht dumm vor, aber zum Glück weiß ja niemand, dass ich solche elementaren Fehler mache. In neuen PHP5-Versionen wird immerhin auch der Name der Funktion ausgegeben:
Mit dem Funktionsnamen werde ich in die richtige Richtung gelenkt und es bleibt nur das Hinzufügen der Begrenzer. Viel gefährlicher aber sind die Situationen, bei denen keine Fehlermeldung ausgegeben wird. Hätte die Regex aus dem obigen Beispiel so gelautet:
Dann hätte preg_match die spitzen Klammern als Begrenzungszeichen-Paar interpretiert und ˹(\w+)(.*?)˼ als völlig korrekten regulären Ausdruck. Nur hätte der Code auch Treffer gefunden, wo ich keine erwartet hätte. Diese lautlosen Fehler sind die heimtückischsten. |
<< zurück | vor >> |
Tipp der data2type-Redaktion: Zum Thema Reguläre Ausdrücke bieten wir auch folgende Schulungen zur Vertiefung und professionellen Fortbildung an: |
Copyright der deutschen Ausgabe © 2008 by O’Reilly Verlag GmbH & Co. KG
Für Ihren privaten Gebrauch dürfen Sie die Online-Version ausdrucken.
Ansonsten unterliegt dieses Kapitel aus dem Buch "Reguläre Ausdrücke" denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.
O’Reilly Verlag GmbH & Co. KG, Balthasarstr. 81, 50670 Köln