Schritt für Schritt Reguläre Ausdrücke verstehen Einstieg in Reguläre Ausdrücke O REILLY Michael Fitzgerald Übersetzung von Thomas Demmig
Inhalt Vorwort.... 1 Was ist ein regulärer Ausdruck?... 1 Ein Anfang mit Regexpal... 2 Eine nordamerikanische Telefonnummer finden... 3 Ziffern mit einer Zeichenklasse finden... 4 Ein Zeichenkürzel verwenden... 5 Beliebige Zeichen finden... 5 Gruppen und Rückwärtsreferenzen... 6 Quantoren einsetzen... 7 Einfassende Literale... 8 Eine Beispielanwendung... 9 Was Sie in Kapitel 1 gelernt haben... 11 Technische Hinweise... 11 2 Einfache Muster finden... 13 Stringliterale finden... 15 Ziffern finden... 15 Etwas anderes als Ziffern finden... 17 Wort- und Nicht-Wortzeichen finden... 18 Whitespace finden... 20 Beliebige Zeichen finden Zweite Runde... 22 Text mit Markup versehen... 24 Text mit sed mit Markup versehen... 24 Markup mit Perl einfügen... 25 Was Sie in Kapitel 2 gelernt haben... 27 Technische Hinweise... 27 IX V
3 Grenzen... 29 Anfang und Ende einer Zeile... 29 Wort- und Nicht-Wortgrenzen... 31 Andere Anker... 33 Eine Gruppe von Zeichen als Literale markieren... 34 Tags hinzufügen... 35 Tags mit sed hinzufügen... 36 Tags mit Perl hinzufügen... 37 Was Sie in Kapitel 3 gelernt haben... 38 Technische Hinweise... 38 4 Alternation, Gruppen und Rückwärtsverweise... 41 Alternation... 41 Untermuster... 45 Einfangende Gruppen und Rückwärtsverweise... 46 Benannte Gruppen... 48 Nicht-einfangende Gruppen... 49 Atomare Gruppen... 50 Was Sie in Kapitel 4 gelernt haben... 50 Technische Hinweise... 51 5 Zeichenklassen... 53 Negierte Zeichenklassen... 55 Vereinigung und Differenz... 56 POSIX-Zeichenklassen... 58 Was Sie in Kapitel 5 gelernt haben... 59 Technische Hinweise... 60 6 Unicode- und andere Zeichen finden... 61 Ein Unicode-Zeichen finden... 62 Einsatz von vim... 63 Zeichen mit Oktalzahlen finden... 65 Eigenschaften von Unicodezeichen finden... 65 Steuerzeichen finden... 68 Was Sie in Kapitel 6 gelernt haben... 70 Technische Hinweise... 71 VI Inhalt
7 Quantoren... 73 Gierig, genügsam und possessiv... 74 Mit *, + und? finden... 74 Eine bestimmte Anzahl finden... 75 Genügsame Quantoren... 77 Possessive Quantoren... 78 Was Sie in Kapitel 7 gelernt haben... 79 Technische Hinweise... 79 8 Lookarounds... 81 Positive Lookaheads... 81 Negative Lookaheads... 84 Positive Lookbehinds... 85 Negative Lookbehinds... 85 Was Sie in Kapitel 8 gelernt haben... 86 Technische Hinweise... 86 9 Ein Dokument mit HTML-Markup versehen... 87 Tags finden... 87 Reinen Text mit sed umformen... 88 Mit sed ersetzen... 89 Römische Zahlen mit sed verarbeiten... 90 Einen bestimmten Absatz mit sed verarbeiten... 91 Die Zeilen des Gedichts mit sed verarbeiten... 91 Tags anfügen... 92 Eine Befehlsdatei bei sed einsetzen... 92 Reinen Text mit Perl umwandeln... 94 Römische Zahlen mit Perl verarbeiten... 95 Einen bestimmten Absatz mit Perl verarbeiten... 96 Die Zeilen des Gedichts mit Perl verarbeiten... 96 Eine Befehlsdatei mit Perl einsetzen... 97 Was Sie in Kapitel 9 gelernt haben... 98 Technische Hinweise... 98 10 Das Ende vom Anfang... 101 Mehr erfahren... 103 Interessante Tools, Implementierungen und Bibliotheken... 103 Perl... 103 PCRE... 104 Inhalt VII
Ruby (Oniguruma)... 104 Python... 105 RE2... 105 Eine nordamerikanische Telefonnummer finden... 105 Eine E-Mail-Adresse finden... 106 Was Sie in Kapitel 10 gelernt haben... 107 Anhang: Reguläre Ausdrücke Referenz... 109 Glossar zu regulären Ausdrücken... 123 Index.... 127 VIII Inhalt
KAPITEL 8 Lookarounds Lookarounds sind nicht-einfangende Gruppen, die Text finden, der sich entweder vor oder hinter einem anderen Muster befinden. Lookarounds werden ebenfalls als Zusicherungen der Breite Null betrachtet. Zu Lookarounds gehören: Positive Lookaheads Negative Lookaheads Positive Lookbehinds Negative Lookbehinds In diesem Kapitel werde ich Ihnen jeden dieser Lookarounds erklären. Wir beginnen wieder mit RegExr auf dem Desktop und wechseln dann zu Perl und ack (grep kennt keine Lookarounds). Unser Text wird immer noch das Gedicht von Coleridge sein. Positive Lookaheads Stellen Sie sich vor, Sie wollen jedes Vorkommen des Worts ancyent finden, auf das marinere folgt (Ich nutze diese alte Schreibweise, weil sie so im Gedicht vorhanden ist). Dazu können wir einen positiven Lookahead verwenden. Lassen Sie uns dies zunächst im RegExr Desktop ausprobieren. Das folgende Muster, das Groß- und Kleinschreibung ignoriert, wandert in das obere Textfeld: (?i)ancyent (?=marinere) Sie können in RegExr auch die Checkbox ignorecase markieren, um Großund Kleinschreibung zu ignorieren. Da Sie die Option zum Ignorieren von Groß- und Kleinschreibung verwenden (?i), müssen Sie sich keine Gedanken darum machen, welche Schreibweise Sie in Ihrem Muster einsetzen. Sie suchen nach jeder Zeile, in der das Wort ancyent gefolgt von marinere steht. 81
Die Ergebnisse werden im Textfeld unter dem Musterbereich hervorgehoben (siehe Abbildung 8-1). Allerdings wird nur der erste Teil des Musters blau hinterlegt (ancyent), nicht das Lookahead-Muster (Marinere). Abbildung 8-1: Positiver Lookahead in RegExr Lassen Sie uns nun mit Perl einen positiven Lookahead verwenden. Den Befehl können Sie so schreiben: perl -ne 'print if /(?i)ancyent (?=marinere)/' rime.txt Die Ausgabe sollte wie folgt aussehen: THE RIME OF THE ANCYENT MARINERE, IN SEVEN PARTS. How a Ship having passed the Line was driven by Storms to the cold Country towards the South Pole; and how from thence she made her course to the tropical Latitude of the Great Pacific Ocean; and of the strange things that befell; and in what manner the Ancyent Marinere came back to his own Country. It is an ancyent Marinere, "God save thee, ancyent Marinere! "I fear thee, ancyent Marinere! Im Gedicht gibt es gibt fünf Zeilen, in denen das Wort ancyent direkt vor dem Wort marinere steht. Was machen wir, wenn wir nur prüfen wollen, ob das Wort nach ancyent mit dem Buchstaben m beginnt egal, ob in Groß- oder Kleinschreibung? Das geht zum Beispiel so: perl -ne 'print if /(?i)ancyent (?=m)/' rime.txt 82 Kapitel 8: Lookarounds
Neben Marinere wird nun auch man und Man gefunden: And thus spake on that ancyent man, And thus spake on that ancyent Man, ack kennt ebenfalls Lookarounds, da es in Perl geschrieben ist. Die Befehlszeilenschnittstele von ack ähnelt stark der von grep. Versuchen Sie: ack '(?i)ancyent (?=ma)' rime.txt Sie erhalten dann die hervorgehobenen Ergebnisse, wie in Abbildung 8-2 zu sehen. Abbildung 8-2: Positiver Lookahead mit ack im Terminal Mit ack können Sie ein Ignorieren von Groß- und Kleinschreibung auch über die Befehlszeilenoption -i aktivieren, statt die eingebettete Option (?i) zu nutzen: ack -i 'ancyent (?=ma)' rime.txt Ich möchte Ihnen zu ack noch ein paar Tipps geben. Wollen Sie bei der Ausgabe Zeilennummern haben, geht das auf unterschiedlichen Wegen. So können Sie zum Beispiel die Option -H verwenden: ack -Hi 'ancyent (?=ma)' rime.txt Positive Lookaheads 83
Oder Sie fügen diesen Code mit der Option --output hinzu: ack -i --output '$.:$_' 'ancyent (?=ma)' rime.txt Das ist mehr oder weniger ein Hack und das Hervorheben der gefundenen Texte funktioniert nicht mehr, aber es ist vielleicht auch eine Ausgangsbasis für eigene Erweiterungen. Negative Lookaheads Das Gegenstück zu einem positiven Lookahead ist ein negativer Lookahead. Das bedeutet, Sie suchen nach einem Muster, bei dem ein bestimmtes Lookahead-Muster eben nicht folgt. Ein negativer Lookahead sieht wie folgt aus: (?i)ancyent (?!marinere) Hier hat sich nur ein Zeichen geändert: Das Gleichheitszeichen (=) im positiven Lookahead wurde zu einem Ausrufezeichen (!) im negativen Lookahead. Abbildung 8-3 zeigt diesen negativen Lookahead in Opera. Abbildung 8-3: Negativer Lookahead mit RegExr in Opera In Perl können wir einen negativen Lookahead wie folgt einsetzen: perl -ne 'print if /(?i)ancyent (?!marinere)/' rime.txt 84 Kapitel 8: Lookarounds
Das Ergebnis ist: And thus spake on that ancyent man, And thus spake on that ancyent Man, In ack lässt sich das gleiche Ergebnis auf diese Art und Weise erreichen: ack -i 'ancyent (?!marinere)' rime.txt Positive Lookbehinds Ein positiver Lookbehind schaut nach links, in die Gegenrichtung eines Lookahead. Die Syntax ist: (?i)(?<=ancyent) marinere Der positive Lookbehind nutzt ein Kleinerzeichen (<), so dass man erinnert wird, in welche Richtung der Lookbehind schaut. Probieren Sie dies einmal in RegExr aus und schauen Sie sich den Unterschied an. Statt dass ancyent hervorgehoben wird, geschieht dies nun mit marinere. Warum? Weil es sich beim positiven Lookbehind um eine Bedingung für die Übereinstimmung handelt, die aber nicht selbst in die Suchergebnisse eingebunden wird. In Perl sieht es so aus: perl -ne 'print if /(?i)(?<=ancyent) marinere/' rime.txt Und so in ack: ack -i '(?<=ancyent) marinere' rime.txt Negative Lookbehinds Schließlich gibt es noch negative Lookbehinds. Wie werden die wohl funktionieren? Negative Lookbehinds schauen nach, ob ein Muster nicht vor dem eigentlichen Muster auftaucht. Auch hier ist es wieder das Kleinerzeichen (<), mit dem Sie die Richtung erkennen. Probieren Sie dies in RegExr aus, um sich die Ergebnisse anzuschauen. (?1)(?<!ancyent) marinere Wenn Sie im Zieltext nach unten scrollen, finden Sie die hervorgehobenen Suchergebnisse. Dann ein Versuch in Perl: perl -ne 'print if /(?i)(?<!ancyent) marinere/' rime.txt Negative Lookbehinds 85