Erweiterung der Fundamente

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

Ich hoffe, die bisherigen Beispiele und Erläuterungen haben eine solide Basis geschaffen für das Verständnis von regulären Ausdrücken. Aber es muss gesagt sein, dass wir noch nirgends wirklich in die Tiefe der Materie vorgedrungen sind. Es gibt noch viel zu lernen!

Linguistisches Divertissement

Ich habe eine Anzahl Features erwähnt, die fast jedes egrep kennt. Aber es gibt mehr davon, und nicht jede Version unterstützt alle diese Features.

Unglücklicherweise unterscheidet sich die Sprache der regulären Ausdrücke dabei in keiner Weise von natürlichen Sprachen: Es gibt die verschiedensten Dialekte, von Akzenten und Unterschieden in der Betonung gar nicht zu reden. Es macht fast den Eindruck, als ob jedes neue Programm, das reguläre Ausdrücke unterstützt, seine eigenen »Verbesserungen« anbringt. Natürlich ist das Gebiet im Fluss, aber über die Jahre hat sich eine breite Palette von Dialekten ausgebildet. Wir werden etliche davon kennenlernen.

Das Ziel eines regulären Ausdrucks

Global gesehen passt ein regulärer Ausdruck entweder auf ein Stück Text (bei egrep: eine Zeile), oder er passt eben nicht. Wenn Sie einen regulären Ausdruck zusammenstellen, müssen Sie sich dieses Seilziehen zwischen »passen« und »nicht passen« öfter vergegenwärtigen.

egrep kümmert sich nicht darum, wo auf einer Zeile ein Muster gefunden wird: Wenn es irgendwo auf der Zeile vorkommt, wird die Zeile ausgegeben. Bei anderen Programmen kann das sehr wohl eine Rolle spielen. Falls der zu prüfende Text etwa so aussieht

...Postleitzahl 50670. Bitte 4,50 Euro in Briefmarken für Rückporto...

und Sie nur nach Zeilen mit Ziffern suchen (˹[0-­9]+˼), dann kann es Ihnen egal sein, welche der Zahlen einen Treffer erzeugt. Wenn Sie dagegen die gefundenen Zahlen weiterverarbeiten wollen (sie in eine Datei schreiben, zu der Zahl etwas addieren usw.), dann ist es ziemlich wichtig, welche der Zahlen einen Treffer bewirkt. Schließlich spielt es schon eine Rolle, ob Sie Briefmarken für 50 670 Euro oder für 4,50 Euro kaufen.

Weitere Beispiele

Wie bei jeder Sprache ist Übung sehr wichtig. Daher gebe ich hier ein paar Beispiele, die Suchmuster für häufige Konstrukte behandeln.

Beim Konstruieren von regulären Ausdrücken ist das Finden von Mustern die halbe Miete. Die andere Hälfte ist natürlich das Vermeiden von unerwünschten Treffern. Beides ist wichtig, aber für den Moment will ich das Augenmerk auf die erste Hälfte lenken. Wir werden die Beispiele nicht bis ins letzte Detail ausloten, weil es vor allem darum geht, Übung im Umgang mit regulären Ausdrücken zu erlangen.

Variablennamen

Viele Programmiersprachen haben Bezeichner (für Variablen und Ähnliches), die aus alphanumerischen Zeichen und dem Unterstrich zusammengesetzt sind, die aber nicht mit einer Ziffer beginnen dürfen, also ˹[a-­zA-­Z_][a-­zA-­Z_0-­9]*˼. Die erste Zeichenklasse ergibt ein Muster für den ersten Buchstaben, die zweite (mit dem zugehörigen Stern) eines für die restlichen Zeichen des Bezeichners. Falls ein Limit für die Länge der Bezeichner besteht (oft 32) und falls unser Werkzeug die ˹{min, max-Notation versteht, kann der Stern durch ˹{0,31}˼ ersetzt werden (dieses Konstrukt, das Intervall, wurde kurz im Abschnitt Explizites Minimum und Maximum: Intervalle erwähnt).

Ein String in Anführungszeichen

Eine einfache Lösung könnte sein: ˹"[^"]*"˼.

Die Anführungszeichen zu Anfang und Ende des Ausdrucks müssen so im Text erscheinen. Dazwischen darf alles vorkommen ... außer ein weiteres Anführungszeichen! Also benutzen wir die negierte Zeichenklasse ˹[^"]˼ und einen Stern, der besagt, dass es von diesen Nicht- Anführungszeichen beliebig viele geben darf.

Eine weiter gehende (und kompliziertere) Denition eines Strings in Anführungszeichen erlaubt solche Gänsefüßchen auch im Innern des Strings, allerdings müssen diese dann durch einen vorangestellten Backslash geschützt sein; etwa "^oder\"Hütchen\"oderZirkumflex". Wir werden in späteren Kapiteln auf diese schwierigere Interpretation zurückkommen, wenn wir untersuchen, wie die Mustersuche genau vor sich geht.

Heller und Pfennig

Hier folgt ein Ansatz für einen regulären Ausdruck für gebrochene Geldbeträge (wir nehmen hier Dollar und Cent): ˹\$[0-­9]+(\.[0-­9][0-­9])?˼.

Von außen nach innen vorgehend, ist dies zunächst einmal ein regulärer Ausdruck aus drei Teilen: ˹\$˼ und ˹...+˼ und ˹(...)?˼. Salopp übersetzt bedeutet er etwa: »Ein Dollarzeichen, ein Dingsda oder ein paar davon und am Schluss vielleicht ein anderes Dingsda«. In diesem Fall ist das erste »Dingsda« eine Ziffer (mehrere Ziffern ergeben eine Zahl), und das »andere Dingsda« ist die Kombination von einem Dezimalpunkt und zwei Ziffern.

Dieser Ansatz ist aus mehreren Gründen etwas naiv. Zunächst werden Geldbeträge manchmal in Dreiergruppen aufgeteilt (in den USA mit Kommas, in Deutschland mit Punkten, in der Schweiz mit Hochkommas): $1,000. Mit egrep interessiert uns im Allgemeinen nur, ob ein Dollar-Betrag auf der Zeile vorkommt; ob er nun Cent-Beträge enthält, ist irrelevant: Die Zeile wird auf jeden Fall ausgegeben. Bei egrep spielt es keine Rolle, wie viel vom Text auf den regulären Ausdruck passt, es ist nur wichtig, ob der Ausdruck überhaupt passt oder nicht.

Falls wir aber an Zeilen interessiert sind, die nichts außer diesem Dollar-Betrag enthalten, würden wir den regulären Ausdruck in ˹^...$˼ einkleiden. In diesem Fall sind die optionalen Cent-Beträge sehr wohl wichtig, weil sie zwischen dem ganzen Dollar-Betrag und dem Zeilenende vorkommen können –- oder auch nicht.

Eine Art von Dollar-Beträgen erkennt unser regulärer Ausdruck nicht: ››$.49‹‹. Um dieses Problem zu lösen, ist man versucht, das Plus durch einen Stern zu ersetzen –- aber das geht schief. Warum? Das lasse ich erst einmal offen, bis wir uns unter Regex-Methoden aus der Praxis mit einem ganz ähnlichen Problem befassen.

Eine HTTP/HTML-URL

Das allgemeine Format für eine URL ist erstaunlich kompliziert, und deshalb ist es auch der entsprechende reguläre Ausdruck. Wenn wir unsere Ansprüche etwas herunterschrauben und nur die häufigsten Typen von URLs betrachten, wird es deutlich einfacher. Eine Anwendung dafür wäre etwa die Suche im eigenen E-Mail-Archiv nach einer URL, an die man sich nur noch undeutlich erinnert; dabei ist formale Korrektheit nicht wichtig, es geht nur darum, viele URLs auszugeben. Die gesuchten URLs findet man beim schnellen Durchlesen der Ausgabe von egrep.

»Normale« HTTP/HTML-URLs haben etwa die folgende generelle Form:

http://servername/pfad.html

Auch die Endung .htm kommt häufig vor.

Welchen genauen Regeln ein Hostname (hier der servername, zum Beispiel www.yahoo.com) genügen muss –- auch das ist nicht ganz so einfach. Im Moment genügt uns ˹[­a-­z0-­9_.]+˼ für das, was nach dem ››http://‹‹ folgt. Der pfad kann noch vielgestaltiger sein, wir benutzen hier ˹[­a-­z0-­9_:@&?=+,.!/~*%$]*˼. Beachten Sie, dass beide Zeichenklassen mit dem Bindestrich als Literal beginnen , sonst würde er als Zeichen für einen Bereich interpretiert.

Zusammengesetzt sieht unser erster Ansatz folgendermaßen aus:

% egrep -­i '\<http://[­-a-­z0-­9_.]+/[­-a-­z0-­9_:@&?="+,.!/~*%$]*\.html?\>' dateien ...

Wieder sind wir ziemlich großzügig und lassen viel mehr zu, als eigentlich erlaubt ist. Der Ausdruck würde auch auf »http://..../foo.html« passen, was sicher keine zulässige URL ist. Ist das ein Problem? Das kommt ganz darauf an, was Sie eigentlich erreichen wollen. Beim Durchsuchen von E-Mails spielt es sicher keine Rolle, wenn wir ein paar falsche Treffer bekommen. Wir kämen auch mit etwas Einfacherem wie

% egrep -­i '\<http://[^ ]*\.html?\>' dateien ...

gut zurecht. Es ist wichtig zu wissen, wie die erwarteten Daten aussehen. Wenn wir lernen, wie man einen regulären Ausdruck aufbaut, wird es immer wichtiger, die Balance zwischen Komplexität und Vollständigkeit zu halten. Wir werden das Beispiel im nächsten Kapitel noch gründlicher studieren.

Ein HTML-Tag

Mit einem Werkzeug wie egrep ist es nicht besonders sinnvoll, nach Zeilen mit HTML-Tags zu suchen. Dennoch wird ein regulärer Ausdruck, der HTML-Tags erkennt, sehr oft benötigt, gerade in Programmiersprachen, die in den späteren Kapiteln behandelt werden.

Wenn wir einfache Fälle wie ›<TITLE>‹ oder ›<HR>‹ betrachten, drängt sich ein einfacher Ausdruck wie ˹<.*>˼ auf. Fast jeder kommt auf diese Idee, aber sie ist falsch. Der Ausdruck ˹<.*>˼ lautet in Worten: »Suche nach einem ››<‹‹, gefolgt von irgendetwas, gefolgt von ››>‹‹.« So gesehen wird klar, dass der Ausdruck auch auf mehrere Tags auf einmal passen kann, wie beim unterstrichenen Teil in ››ein <I>kleines</I> Beispiel‹‹.

Das mag erstaunen, aber wir sind noch immer im ersten Kapitel und verstehen reguläre Ausdrücke erst ganz oberflächlich. Das Beispiel soll einerseits zeigen, dass reguläre Ausdrücke nicht besonders schwierig sind, dass sie aber dann schwierig werden, wenn man nicht versteht, was eigentlich vor sich geht. In den nächsten Kapiteln betrachten wir alle Einzelheiten von regulären Ausdrücken, um dieses Problem zu verstehen und zu lösen.

Amerikanische Uhrzeiten wie »9:17 am« oder »12:30 pm«

Das Problem »amerikanische Uhrzeiten erkennen« (Anmerkung: US-Amerikaner haben große Mühe damit, dass ein Tag 24 Stunden haben könnte. Sie bevorzugen meistens die Zwölfstunden-Notation und setzen ein »am« (manchmal »a.m.« oder nur »a«, ante meridiem, vormittags) oder ein »pm« (post meridiem, nachmittags) dahinter. Fast völlig unfassbar sind dem Amerikaner Null-Uhrzeiten wie 0.13 oder 12.59, er wird dafür »12:13 pm« und »12:59 am« schreiben, obwohl 12.59 entschieden nicht mehr zum Vormittag gehört. (Anm. d. Ü.)) kann man von salopp bis sehr strikt behandeln. Ein Ausdruck wie

˹[0-­9]?[0-­9]:[0-­9][0­-9](am|pm)˼

erkennt sowohl 9:17am als auch 12:30pm, aber er erlaubt auch Unsinniges wie 99:99pm.

Wenn wir die Stundenzahl anschauen, stellen wir fest, dass die erste Zahl eine 1 sein muss, wenn die Stundenzahl zweistellig ist. Aber ˹1?[0­-9]˼ erlaubt noch immer eine Stundenzahl von 19 (und auch 0), also ist es vielleicht gescheiter, den Stunden-Teil in zwei Alternativen aufzuteilen: ˹1[012]˼ für zweistellige Stundenzahlen und ˹[1­-9]˼ für einstellige. Daraus resultiert ˹(1[012]|[1­-9])˼.

Der Minuten-Teil ist einfacher. Die erste Ziffer muss auf ˹[0­-5]˼ passen, die zweite Ziffer können wir bei ˹[0­-9]˼ belassen. Wenn wir alle einzelnen Ausdrücke kombinieren, ergibt sich ˹(1[012]|[1-­9]):[0­-5][0-­9](am|pm)˼.

Wie erweitert man das auf das anderswo übliche 24-Stunden-Schema, mit Stundenzahlen von 0 bis 23? Als zusätzliches Erschwernis sollen hier mit null beginnende Stundenzahlen wie 09:59 erlaubt sein. Auflösung siehe Lösung 4. Versuchen Sie, Ihre eigene Lösung zu finden, bevor Sie auf den Link klicken.

Reguläre Ausdrücke: Terminologie

Regex

Wie Ihnen zweifellos aufgefallen ist, ist die dauernde Wiederholung der vollen Bezeichnung »regulärer Ausdruck« auf die Dauer ermüdend. Ich benutze dafür normalerweise »Regex«, die Kurzform für das englische »regular expression«. Manchmal spreche ich davon, einen Gedanken ins »Regexische« zu übersetzen, oder benutze gar »regexizieren«. (Anmerkung: Sie werden anderswo auch das unschöne »Regexp« antreffen. Ich kann das kaum aussprechen; Lispelnde haben damit wahrscheinlich keine Mühe.) Die Bezeichnung »Regex-Maschine« oder einfach »Maschine« benutze ich für das Programm, das die eigentliche Arbeit des Pattern-Matchings verrichtet.

Pattern-Matching

Wenn ich bei der Mustersuche oder dem Pattern-Matching von einem Treffer rede, dann meine ich, dass die Regex-Maschine einen passenden String in dem zu untersuchenden Text gefunden hat. Genauer gesagt, passt die Regex ˹i˼ nicht auf den String ding, sie passt nur auf das i im Text ding. Dies mehr der Vollständigkeit halber; es bestehen diesbezüglich kaum Verständnisschwierigkeiten.

Metazeichen

Ob ein Zeichen ein Metazeichen (oder eine Metasequenz, ich benutze beide Begriffe unterschiedslos) ist oder nicht, hängt davon ab, wo genau in der Regex das Zeichen vorkommt. Beispielsweise ist ˹*˼ ein Metazeichen, aber nur außerhalb von Zeichenklassen und nur, wenn es nicht durch einen vorangestellten Backslash geschützt ist. Ein »Escape« ist ein durch einen vorangestellten Backslash geschütztes Zeichen –- meistens. In ˹\*˼ bilden Stern und Backslash ein Escape, aber nicht in ˹\\*˼ (der erste Backslash schützt hier den zweiten vor seiner üblichen Interpretation), obwohl der Stern beide Male ein vorangestelltes Escape-Zeichen besitzt.

Aufgrund der verschiedenen Regex-Dialekte kann ein Zeichen einmal als Metazeichen auftreten und ein andermal nicht. Features und Dialekte zeigt das im Detail.

Dialekte

Wie schon angedeutet, benutzen die verschiedenen Werkzeuge reguläre Ausdrücke für sehr unterschiedliche Dinge, und die unterstützten Metazeichen können sehr verschieden sein. Als Beispiel dienen hier wiederum die Wortgrenzen: Manche Versionen von egrep unterstützen dafür die \<...\>-Notation. Andere unterscheiden nicht zwischen Wortanfang und Wortende, sondern kennen nur ein Metazeichen, ˹\b˼, für »Wortgrenze«. Noch andere kennen alle drei, und natürlich gibt es auch solche, die keines dieser Metazeichen kennen.

Ich benutze den Begriff »Dialekt«, um die Summe dieser kleineren Implementationsunterschiede zu bezeichnen, dies selbstverständlich in Analogie zu natürlichen Sprachen. Oberflächlich betrachtet, geht es nur darum, ob bestimmte Metazeichen unterstützt werden oder nicht, aber es steckt mehr dahinter. Auch wenn zwei Programme die ˹\<...\>˼-Metazeichen kennen, können sie noch immer verschiedene Vorstellungen darüber haben, was denn nun ein Wort ausmacht. Das wird wichtig, wenn ein Programm wirklich benutzt und nicht nur evaluiert wird.

»Dialekt« bedeutet aber nicht das Gleiche wie »Werkzeug« oder »Programm«. So wie zwei Personen die gleiche Mundart sprechen können, gibt es völlig unterschiedliche Programme, die den gleichen Dialekt von regulären Ausdrücken verwenden. Umgekehrt gibt es verschiedene Programme gleichen Namens (und mit dem gleichen Zweck), die subtil unterschiedlichen Mikrodialekten angehören (manchmal sind die Unterschiede auch gar nicht so subtil). Die vielen egrep-Implementationen unterstützen fast ebenso viele Dialekte von regulären Ausdrücken.

In den späten Neunzigern wurde die Programmiersprache Perl mit ihrem sehr reichen Dialekt von regulären Ausdrücken sehr bekannt. Bald haben andere Programmiersprachen von Perl abgekupfert. Manche geben das ganz offen zu und nennen ihre Implementation »Perlkompatibel«. Die Nachahmer sind PHP, Python, viele Regex-Packages für Java, .NET von Microsoft, Tcl und eine ganze Reihe von Bibliotheksroutinen für C. Dennoch unterscheiden sich viele in kleinen, aber wichtigen Aspekten. Außerdem werden auch die regulären Ausdrücke von Perl weiterentwickelt -– manchmal werden neuerdings auch Ideen der Nachahmer aufgenommen. Die Landschaft der regulären Ausdrücke verändert sich, wird weiter und damit nicht immer einfacher.

Unterausdruck

Die Bezeichnung Unterausdruck bedeutet zunächst einfach einen Teil eines größeren regulären Ausdrucks, meistens aber etwas spezischer einen geklammerten Teil oder eine Alternative innerhalb einer Alternation. Zum Beispiel ist ˹Subject|Date˼ ein Unterausdruck von ˹^(Subject|Date):˼. Innerhalb dieses Ausdrucks sind die Alternativen ˹Subject˼ und ˹Date˼ ebenfalls Unterausdrücke.

Dagegen ist etwas wie ˹1­-6˼ kein Unterausdruck von ˹H[1­-6]l*˼, weil das ˹1­-6˼ Teil einer »Einheit «, einer Zeichenklasse ist, die man nicht weiter zerlegen kann. Umgekehrt sind ˹H˼, ˹[1­-6]˼ und ˹*˼ sehr wohl Unterausdrücke des Originals.

Die Quantoren (Stern, Plus, Fragezeichen) beziehen sich immer auf den unmittelbar vorausgehenden Unterausdruck. Deswegen kontrolliert das + in ˹vertip+t˼ nur das ˹p˼ und nicht etwa ˹vertip˼ oder ˹ip˼ . Wenn dem Quantor unmittelbar ein geklammerter Unterausdruck vorangeht, dann bezieht er sich natürlich auf diesen ganzen Unterausdruck, ungeachtet dessen, wie komplex er aufgebaut ist.

Zeichen

In der Informatik kann Zeichen je nach Zusammenhang ganz verschiedene Dinge bedeuten. Was für ein Zeichen ein bestimmtes Byte darstellt, ist weitgehend Interpretationssache. Ein Byte mit einem bestimmten Wert hat dagegen immer diesen Wert, ganz egal in welchem Kontext. Welches Zeichen damit dargestellt werden soll, hängt vom verwendeten Zeichensatz ab. Zwei Bytes mit den (dezimalen) Werten 64 und 53 repräsentieren die Zeichen »@« und »5«, wenn sie als ASCII-Zeichen aufgefasst werden, aber etwas völlig anderes, wenn als Zeichensatz EBCDIC verwendet wird (dann sind es das Leerzeichen und ein Kontrollzeichen).

Wenn die zwei Bytes aber in einem der für das Japanische gebräuchlichen Zeichensätze aufgefasst werden, bilden sie nur ein einziges Zeichen, 正. Um im EUC-JP-Zeichensatz das Zeichen 正 darzustellen, braucht es wiederum zwei ganz andere Bytes. Diese zwei Bytes, im Latin-1-Zeichensatz (ISO-8859-1) dargestellt, ergeben die zwei Zeichen »Àμ« oder das eine koreanische Zeichen  im Unicode-Zeichensatz. (Anmerkung: Das Standardwerk über Multibyte-Zeichensätze ist CJKV Information Processing von Ken Lunde, auch bei O'Reilly erschienen. Das CJKV steht für Chinesisch, Japanisch, Koreanisch und Vietnamesisch, alles Sprachen, die Multibyte-Zeichensätze erfordern. Von Ken und von der Firma Adobe habe ich die speziellen Zeichensätze für dieses Buch erhalten.) Entscheidend ist, dass es vom Standpunkt abhängt, wie viele Bytes ein Zeichen ausmachen (und so ein Encoding bilden). Beim Vergleichen von Zeichen muss sichergestellt sein, dass Ihr Standpunkt mit dem des verwendeten Werkzeugs oder der Programmiersprache übereinstimmt.

Bis vor Kurzem haben fast alle Regex-Werkzeuge ihre Daten als Ansammlung von 8-Bit-Bytes angesehen und sich nicht oder kaum um das Encoding gekümmert. Immer häufiger verwenden die Programme aber intern die eine oder andere Abart von Unicode (unter Features und Dialekte finden Sie eine Einführung zu Unicode). Auf solchen Systemen braucht sich der Benutzer normalerweise nicht um diese Belange zu kümmern, wenn der Regex-Teil richtig implementiert ist. Wenn das Wörtchen »wenn« nicht wär ... gerade deshalb befasst sich Features und Dialekte genauer mit dieser Thematik.

Den Status quo verbessern

Wenn man sich ernsthaft mit ihnen befasst, sind reguläre Ausdrücke nicht besonders schwierig. Aber wenn man mit Benutzern von Regex-Programmen spricht, wird man oft Leute finden, die »ein bisschen was davon« verstehen, sich aber nicht sicher genug im Umgang damit fühlen, um reguläre Ausdrücke für komplexe Aufgaben einzusetzen oder sie in nicht so häufig benutzten Programmen zu gebrauchen.

Die traditionelle Dokumentation zu regulären Ausdrücken besteht oft nur aus einer kurzen Beschreibung von ein, zwei Metazeichen, gefolgt von einer Tabelle der restlichen. Man sieht Beispiele wie ˹a*((ab)*|b*)˼ und sinnlose Beispieldaten wie ››axxxcexxxxxxcixxxd‹‹ ohne Bezug zur Praxis. Meist bleiben die subtilen, aber wichtigen Punkte unerwähnt; oder es wird behauptet, der Dialekt sei derselbe wie in einem anderen, bekannten Programm, ohne die Unterschiede anzugeben, die fast zwangsläufig vorhanden sind. Diesem Zustand muss abgeholfen werden.

Nun bilde ich mir nicht ein, dass dieses einführende Kapitel diese Lücke füllt. Es setzt nur das Fundament, auf dem der Rest des Buches aufbaut. Es mag ambitiös klingen, aber ich hoffe, dass das ganze Buch die Lücke füllt. Ich habe zur ersten Auflage viele erfreuliche Reaktionen erhalten und mir Mühe gegeben, diese zweite Auflage quantitativ und qualitativ zu verbessern.

Vielleicht gerade wegen der traditionell schlechten Dokumentation fühle ich mich verpflichtet, die Dinge bis ins Detail zu beschreiben. Ich möchte, dass Sie das volle Potenzial der regulären Ausdrücke ausschöpfen können und dass Sie reguläre Ausdrücke wirklich, wirklich verstehen.

Das ist gleichzeitig gut und schlecht.

Gut ist es, weil Sie dabei lernen, in regulären Ausdrücken zu denken. Sie werden sehen, auf welche Besonderheiten und Unterschiede man bei Programmen verschiedener Dialekte gefasst sein muss. Sie werden sich auch mit regulären Ausdrücken in einem faden, unvollständigen Dialekt ausdrücken können. Sie werden verstehen, warum ein bestimmter regulärer Ausdruck effizienter ist als ein anderer; Sie können Effizienz gegenüber Komplexität abwägen und Resultate vergleichen. Wenn Sie mit einem komplexen Ausdruck konfrontiert werden, können Sie nachvollziehen, wie dieser Ausdruck vom Programm verarbeitet wird, und Sie können den Ausdruck auf die gleiche Art analysieren. Kurz: Es wird Ihnen ein Leichtes sein, die volle Kraft der regulären Ausdrücke auszunutzen.

Leider ist der Lernaufwand mit dieser Methode nicht gerade gering, denn Sie müssen sich mit drei verschiedenen Bereichen befassen:

Wie reguläre Ausdrücke benutzt werden

Die meisten Programme benutzen reguläre Ausdrücke in weitergehender Art, als wir das bei egrep gesehen haben. Bevor wir uns um die Details kümmern, müssen wir sehen, wie und wo reguläre Ausdrücke eingesetzt werden. Damit befasst sich Erweiterte einführende Beispiele.

Features von regulären Ausdrücken

Die Auswahl des richtigen Programmierwerkzeugs für ein bestimmtes Problem ist oft sehr wichtig und spart Zeit. Ich will mich daher in diesem Buch nicht auf ein einziges Programm beschränken. Nun haben aber verschiedene Programme (und oft sogar verschiedene Versionen desselben Programms) unterschiedliche Features und Metazeichen. Wir müssen uns daher einen Überblick verschaffen, bevor wir diese Features verwenden. Das passiert unter Features und Dialekte.

Wie reguläre Ausdrücke wirklich arbeiten

Um aus nützlichen (und oft komplizierten) regulären Ausdrücken zu lernen, muss man verstehen, wie reguläre Ausdrücke von Programmen abgearbeitet werden. Wir werden sehen, dass es eine Rolle spielt, in welcher Reihenfolge bestimmte Metazeichen geprüft werden. Reguläre Ausdrücke können auf verschiedene Arten implementiert werden, und diese Arten tun manchmal verschiedene Dinge mit dem gleichen Ausdruck. Wir werden uns diesen dicken Brocken unter Wie Regex-Maschinen arbeiten und Regex-Methoden aus der Praxis vornehmen.

Dieser letzte Teil ist der wichtigste, aber auch der am schwierigsten zu behandelnde. Die Beschreibung ist leider manchmal etwas trocken, und man muss sich schon etwas in das Thema verbeißen, bevor wir zum spannenden Teil kommen -– zum Lösen von Problemen aus der Praxis. Aber das Verstehen der Arbeitsweise der Regex-Maschine ist der Schlüssel zum wirklichen Verstehen von regulären Ausdrücken.

Sie werden einwenden, dass man nicht zu verstehen braucht, wie ein Auto funktioniert, um damit fahren zu können. Das mag stimmen, aber Autofahren ist eine schlechte Analogie zu regulären Ausdrücken. Mein Ziel ist es zu zeigen, wie man Probleme mit regulären Ausdrücken löst. Die bessere Analogie ist die zum Konstruieren eines Autos. Und dafür sollte man vielleicht schon wissen, wie ein Auto funktioniert.

Erweiterte einführende Beispiele entspricht etwa ein paar weiteren Fahrstunden. Features und Dialekte plaudert etwas aus der Geschichte des Autos und befasst sich mit Karosserie, Lack und Pflege von regulären Ausdrücken; und unter Wie Regex-Maschinen arbeiten schauen wir den Motor von regulären Ausdrücken an. Regex-Methoden aus der Praxis zeigt einige weitere Beispiele, Die Kunst, reguläre Ausdrücke zu schreiben befasst sich mit der Leistungssteigerung von Motoren; die Kapitel danach gehen genauer auf bestimmte Marken und Modelle ein. Vor allem unter Wie Regex-Maschinen arbeiten, Regex-Methoden aus der Praxis und Die Kunst, reguläre Ausdrücke zu schreiben werden wir einige Zeit »unter dem Auto liegen«, da ist es vielleicht ganz angebracht, Arbeitskleidung zu tragen und Putzfäden zur Hand zu haben.

Zusammenfassung

Die folgende Tabelle fasst die egrep-Metazeichen zusammen, die wir in diesem Kapitel betrachtet haben.

Außerdem sind folgende Punkte wichtig:

  • Nicht alle Programme namens egrep sind gleich. Die unterstützten Metazeichen und deren Bedeutung können verschieden sein –- vgl. die lokale Dokumentation.
  • Drei Gründe für das Setzen von (runden) Klammern sind: Limitieren des Bereichs von Alternationen, Gruppieren und das »Sich-Merken« von Sub-Matches.
  • Zeichenklassen sind speziell: Die Regeln, was ein Metazeichen ausmacht und was nicht, sind darin völlig anders.
  • Alternationen und Zeichenklassen sind fundamental verschieden; nur in pathologisch einfachen Situationen kann deren Wirkung dieselbe sein.

Tabelle: Zusammenfassung - Metazeichen bei egrep.

Metazeichen Beschreibung Passt auf
Dinge, die auf einzelne Zeichen passen
. Punkt Irgendein Zeichen
[...] Zeichenklasse Eines der Zeichen aus der Liste
[^...] Negierte Zeichenklasse Irgendein Zeichen nicht aus der Liste
\Zeichen Escape Wenn Zeichen ein Metazeichen ist oder wenn die Kombination mit Backslash kein neues Metazeichen ergibt: das Literal Zeichen

 Dinge, die »zählen«; Quantoren
? Fragezeichen Einmal erlaubt, aber optional
* Stern Jede Anzahl erlaubt, auch null
+ Plus Mindestens eins, mehr erlaubt
{min, max} Expliziter Bereicha min gefordert, bis und mit max erlaubt

 Dinge, die auf Positionen passen
^ Zirkumflex Passt auf den Zeilenanfang
$ Dollar Passt auf das Zeilenende
\< Wortgrenzea Passt auf die Position am Wortanfang
\> Wortgrenzea Passt auf die Position am Wortende

 Anderes
| Alternation Passt auf mindestens eine der Alternativen
(...) Klammern Beschränkt den Geltungsbereich von Alternationen; gruppiert Dinge für nachfolgende Quantoren; »merkt sich Text« für Rückwärtsreferenzen
\1,\2,... Rückwärtsreferenzena Passt auf Text, der im ersten, zweiten usw. Paar von Klammern vorkam

a wird nicht von allen egrep-Versionen unterstützt

  • Eine negierte Zeichenklasse braucht noch immer ein Zeichen im Suchraum, damit sie passt; sie ist noch immer eine »positive Behauptung«. Die Aufzählung wird negiert, daher muss das passende Zeichen eines sein, das nicht in der Liste vorkommt.
  • Die ­-i-Option bewirkt, dass Groß- und Kleinbuchstaben als identisch angesehen werden.
  • Es gibt drei Arten von Escapes (Zeichen mit einem Backslash davor):

    1. Die Kombination von ˹\˼ mit einem Metazeichen ist eine Metasequenz, die das ursprüngliche Literal ergibt (zum Beispiel ergibt ˹\*˼ einen literalen Stern).
    2. Die Kombination von ˹\˼ mit bestimmten Nicht-Metazeichen erzeugt ein Metazeichen, dessen Bedeutung vom verwendeten Programm abhängt (zum Beispiel bedeutet ˹\<˼ oft »Wortanfang«).
    3. Die Kombination von ˹\˼ mit einem anderen Zeichen ergibt einfach das Zeichen selbst (d. h. der Backslash wird ignoriert).
      Noch einmal: Innerhalb einer Zeichenklasse ist ein Backslash bei den meisten egrep-Versionen ein ganz normales Zeichen und leitet kein Escape ein.

  • Dinge, die mit einem Fragezeichen oder dem Stern quantiziert werden, brauchen gar kein Zeichen im Suchtext, damit die Behauptung erfüllt ist. Sie passen immer, auch wenn sie auf »kein Zeichen« passen.

  

zum Seitenanfang

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