Regex-Modi

(Auszug aus "Reguläre Ausdrücke" von Jeffrey E. F. Friedl)

Die meisten Regex-Maschinen können in verschiedenen Modi arbeiten, je nachdem, wie der reguläre Ausdruck interpretiert oder angewandt werden soll. Wir haben schon Beispiele kennengelernt – der »Freie Form«-Modus von Perl mit dem /x-Modifikator (der Regex-Modus, der Whitespace und Kommentare in der Regex erlaubt, siehe Abschnitt zum x-Modifikator) bezieht sich auf die Interpretation der Regex, der »Groß-/Kleinschreibung ignorieren«-Modus mit dem /i-Modifikator darauf, wie die Regex angewandt wird (siehe Abschnitt zum i-Modifikator).

Diese Art von Modi bezieht sich meist auf die ganze Regex, bei neueren Dialekten auch auf einzelne Unterausdrücke. Wenn sich ein Modus auf die ganze Regex bezieht, geschieht dies gewöhnlich mit einem Modifikator oder mit einer Option, z.B. mit /i in Perl, mit dem i-Modifikator in PHP (siehe Tabelle: Pattern-Modifikatoren in PHP) oder mit Pattern.CASE_INSENSITIVE in Java (siehe Abschnitt Suchen und Ersetzen in Java). Falls dies unterstützt wird, kann man den Modus mit einer Notation wie ˹(?i)˼ innerhalb der Regex ein- und mit ˹(?-i)˼ wieder ausschalten. Manche Dialekte unterstützen auch ˹(?i:...)˼ bzw. ˹(?-i:...)˼, die den »Groß-/Kleinschreibung ignorieren«-Modus nur für den geklammerten Unterausdruck ein- bzw. ausschalten.

Wie diese Modi in einer Regex genau verwendet werden, erfahren Sie später (siehe den Abschnitt Modus-Modifikatoren). Hier werden nur die häufigen Modi kurz vorgestellt.

Der Modus »Groß- und Kleinschreibung ignorieren«

Diesen Modus gibt es praktisch in jeder Programmiersprache und bei jedem Werkzeug. Groß- oder kleingeschriebene Buchstaben gelten als äquivalent, daher passt ˹b˼ sowohl auf ›b‹ als auch auf ›B‹. Dafür muss natürlich die Zeichencodierung richtig eingestellt sein.

Erstaunlicherweise gab und gibt es bei diesem Modus immer wieder Fehler, so funktioniert z.B. in Ruby dieser Modus nicht, wenn ein Buchstabe als oktales oder hexadezimales Escape-Zeichen eingegeben wird.

Im Zusammenhang mit Unicode, wo der Modus »loose matching« heißt, ergeben sich weitere Probleme. Zunächst gibt es nicht in allen Alphabeten so etwas wie Groß- und Kleinbuchstaben, dafür gibt es in anderen Alphabeten Titelbuchstaben, eine Buchstabenform, die nur am Anfang eines Wortes vorkommt und nicht immer mit den normalen Großbuchstaben übereinstimmt. Nicht immer ist die Zuordnung zwischen Groß- und Kleinbuchstabe in beiden Richtungen eindeutig, z.B. gibt es zum griechischen Sigma (Σ) zwei Varianten des Kleinbuchstabens, σ und ς; wenn die Groß- und Kleinschreibung ignoriert wird, sollten alle drei passen (von den Systemen, die ich getestet habe, machen das nur Perl und java.util.regex richtig).

Es gibt auch den Fall, dass aus einem Zeichen zwei werden; der prototypische Fall ist das »ß« und dessen Umschreibung in Großbuchstaben mit »SS«. Dieser Fall wird nur bei Perl korrekt behandelt.

Manche der Probleme bei Unicode sind hausgemacht. Zum Beispiel gibt es einen Codierungspunkt für ǰ (U+01F0), aber für den entsprechenden Großbuchstaben, ˇJ, gibt es nur die Möglichkeit mit dem kombinierenden Zeichen, U+004A und U+030C (siehe den Abschnitt Zeichen und Zeichenkombinationen). Dennoch sollten ǰ und ˇJ im Modus »Groß- und Kleinschreibung ignorieren« äquivalent sein. Es gibt sogar Beispiele, bei denen einem Kleinbuchstaben mit einem einzelnen Codierungspunkt eine Dreiersequenz für den Großbuchstaben entspricht. Zum Glück betrifft das nur selten vorkommende Zeichen.

Der Modus »Freie Form«

In diesem Modus zählt der Whitespace außerhalb von Zeichenklassen nicht (im Package java.util.regex wird auch Whitespace innerhalb von Zeichenklassen ignoriert). Außerdem wird alles von einem # bis zum Ende der Zeile als Kommentar betrachtet. Sie haben dafür bereits Beispiele in Perl (siehe den Abschnitt x-Modifikator), Java (siehe den Abschnitt Suchen und Ersetzen in Java) und in VB.NET (siehe den Abschnitt Suchen und Ersetzen in VB.NET) gesehen.

Eigentlich wird der Whitespace nur in java.util.regex komplett ignoriert. Bei den anderen Implementationen fungiert der Whitespace vielmehr als eine Art »No-Op«-Metazeichen. Das macht beispielsweise bei ˹\123˼ einen Unterschied: In diesem Modus wird das als ˹\12˼, gefolgt von ˹3˼, interpretiert und nicht als ˹\123˼, wie man vielleicht denken könnte.

Was genau »Whitespace« ist und was nicht, hängt natürlich von der verwendeten Zeichencodierung ab und davon, inwiefern die Programmiersprache die Codierung implementiert. Meistens werden nur die zu ASCII gehörenden Whitespace-Zeichen erkannt.

Der Modus »Punkt passt auf alles«

Normalerweise passt der Punkt nicht auf das Newline. Die ursprünglichen Regex-Programme unter Unix bearbeiteten den Text Zeile für Zeile; damit erhob sich die Frage gar nicht, bis sed und lex entstanden. Bis dahin hatte sich informell der Teilausdruck ˹.*˼ als »bis an das Ende der Zeile« etabliert, so dass das Miteinbeziehen des Newline-Zeichens als »zu unhandlich« (Anmerkung: »too unwieldy«, nach den Worten von Ken Thompson, dem Autor von ed.) erachtet wurde. Daher passt auch bei Programmen wie z.B. einem Editor, die mit mehrzeiligen Daten umgehen, der Punkt normalerweise nicht auf das Zeilenende.

In einer Programmiersprache dagegen kann auch ein Punkt, der wirklich auf jedes Zeichen passt, genauso sinnvoll sein. Was in einer bestimmten Situation am besten ist, hängt eben von der Situation ab. Daher kann man den entsprechenden Modus bei vielen Programmiersprachen für jede einzelne Regex wählen.

Es gibt auch Ausnahmen. Bei Unicode-fähigen Systemen wie dem Java-Package von Sun erkennt der Punkt normalerweise keines der Unicode-Zeilenendezeichen, die aus einem einzigen Codierungspunkt bestehen (siehe den Abschnitt Das Zeilenende in Unicode). In Tcl passt der Punkt im Normalfall auf alle Zeichen, auch auf die Zeilenendezeichen; im »Newline-Modus« und im »partiellen Newline-Modus« passen weder der Punkt noch eine negierte Zeichenklasse auf das Newline.

Ein unglücklicher Name

Als der /s-Modifikator in Perl neu war, hieß er »single-line mode« (Einzeilenmodus). Dieser unglückliche Name verfolgt den armen Modifikator bis heute, denn er hat wirklich nichts mit dem ˹^˼ oder dem ˹$˼ zu tun – mit diesen und dem »Mehrzeilenmodus« beschäftigt sich der nächste Abschnitt.

Der Modus »Verbesserte Zeilenanker« oder Mehrzeilenmodus

Im Mehrzeilenmodus bekommen die Zeilenanker ˹^˼ und ˹$˼ eine etwas andere Bedeutung. Normalerweise passt der Anker ˹^˼ nur genau am Anfang des Suchstrings und nicht auf den Anfang von logischen Zeilen innerhalb des Strings, wenn der String mittendrin Newlines enthält. Im Mehrzeilenmodus passt das ˹^˼ auch auf den Anfang von solchen logischen Zeilen: Der String wird korrekt als aus mehreren Zeilen bestehend erkannt. Sie hatten diesen Modus in Erweiterte einführende Beispiele kennengelernt, als wir ein Programm zur Konversion von nacktem Text in HTML entwickelten (siehe Nackten Text in HTML verwandeln). Die ganze Textdatei wurde in einen einzigen String eingelesen, und wir hatten mit der Substitution s/^$/<p>/mg die Leerzeilen wie »...Kapitel.NL NLWenn...« in Paragraph-Tags wie »...Kapitel.NL<p>NLWenn...« verwandelt.

Mit ˹$˼ ist es ganz analog, hier sind allerdings die Regeln, worauf das Dollarzeichen normalerweise passt, etwas komplizierter (siehe den Abschnitt Zeilenende, Stringende: $, \Z, \z). Für den Moment genügt die Erklärung, dass das ˹$˼ im Mehrzeilenmodus auch auf die Position genau vor einem Newline mitten im String passen kann.

Programme, die den Mehrzeilenmodus unterstützen, kennen oft die zusätzlichen Metazeichen ˹\A˼ und ˹\Z˼. Sie verhalten sich normalerweise genauso wie ˹^˼ und ˹$˼ und behalten dieses Verhalten auch im Mehrzeilenmodus. Anders gesagt passt ˹\A˼ immer nur auf den Anfang des Strings, auch im Mehrzeilenmodus. Bei manchen Implementationen passen ˹$˼ oder ˹\Z˼ auch auf die Position genau vor dem Newline am Ende des Suchstrings; diese Implementationen kennen dann oft das zusätzliche Metazeichen ˹\z˼, das sich gar nicht um Newlines kümmert und wirklich nur auf das Ende des Suchstrings passt. Im Abschnitt Zeilenende, Stringende: $, \Z, \z finden Sie alle Details darüber.

Wie beim Punkt gibt es auch hier abweichende Implementationen. Bei einem Editor wie GNU Emacs passen die Zeilenanker normalerweise auf Newlines mitten im Suchtext, das ist bei einem Editor sicher das Vernünftigste. Bei lex dagegen passt das ˹$˼ nur auf die Position vor einem Newline (˹^˼ verhält sich hier normal).

Bei Unicode-fähigen Systemen wie java.util.regex von Sun können die Zeilenanker im Mehrzeilenmodus auf die Position vor bzw. nach jedem der diversen Unicode-Zeilenendezeichen passen (siehe den Abschnitt Das Zeilenende in Unicode). In Ruby ist der Mehrzeilenmodus der voreingestellte Modus, und das ˹\Z˼ in Python verhält sich wie das im letzten Absatz vorgestellte ˹\z˼, nicht wie ˹$˼ wie bei den meisten Programmiersprachen.

Der Name »Mehrzeilenmodus« mag eine Verwandtschaft mit der unglücklichen, zu vermeidenden Bezeichnung »Einzeilenmodus« suggerieren. Eine solche besteht nicht. Im einen Modus wird das Verhalten des Punktes verändert, im anderen das Verhalten von ˹^˼ und ˹$˼. In beiden Modi geht es um die Newline-Zeichen, allerdings aus verschiedenen Blickwinkeln. Im ersten Modus verändert sich die Bedeutung des Newline für den Punkt von »speziell« zu »normal«, im Mehrzeilenmodus verändert sich die Art, wie ˹^˼ und ˹$˼ mit Newlines umgehen, von »normal« zu »speziell«. (Anmerkung: Bei Tcl passt der Punkt normalerweise auf jedes Zeichen, und weder der Punkt noch die Zeilenanker verhalten sich gegenüber dem Newline irgendwie besonders. Mit Optionen kann man dieses Verhalten ändern. Das ist eigentlich viel klarer als bei den anderen Sprachen, aber da es bei den anderen Werkzeugen und Sprachen schon immer so war, behandeln wir das eigentlich logischere Verhalten von Tcl als die Ausnahme.)

Modus für literalen Text

Im Modus »Literaler Text« werden alle oder fast alle Regex-Metazeichen zu normalen Zeichen. In diesem Modus passt beispielsweise ˹[a-z]*˼ auf den String ›[a-z]*‹. Eine Mustersuche im Modus »Literaler Text« wird also zur einfachen Substring-Suche (»Suche nach diesem String« statt »Suche einen Treffer für diese Regex«). Die meisten Programme haben dafür aber eine Suchfunktion, die rein gar nichts mit regulären Ausdrücken zu tun hat. Interessant wird es erst, wenn man den Modus »Literaler Text« für Teile der Regex oder Unterausdrücke selektiv ein- und ausschalten kann. In PCRE (und damit PHP) und in Perl ist das mit der Sequenz \Q...\E möglich, zwischen ˹\Q˼ und ˹\E˼ werden alle Metazeichen zu normalen, literalen Zeichen.

  

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