Das Problem mit den verdoppelten Wörtern aus Kapitel 1 hat hoffentlich
Ihren Appetit auf die Kraft von regulären Ausdrücken geweckt. Ich habe außerdem
am Anfang dieses Kapitels ein paar wilde Zeilen Kode aufgetischt, die
ich die Lösung des Problems genannt habe. Jetzt, da Sie etwas Perl verstehen,
können Sie hoffentlich die generelle Form verstehen das
<>, die drei
s/.../.../
und das print. Es ist wahrscheinlich noch immer verwirrend.
Wenn dies Ihr erster Kontakt mit Perl (und mit regulären Ausdrücken) ist, mag das Folgende
etwas schwer verständlich sein.
Von nahem besehen, sind die regulären Ausdrücke gar nicht so kompliziert. Bevor wir das aber tun, ist es sinnvoll, die Problemstellung genauer zu umschreiben, und ein Beispiel der Ausgabe des fertigen Programms anzusehen:
% perl -w FindDbl kap1.txt kap1.txt: Text aufspürt. Solche Verdoppelungen (wie »das das«) entstehen kap1.txt: Groß- und Kleinschreibung wie bei `Das das...' ignoriert, kap1.txt: fett gedruckt hervorzuheben: `...das ist <B>sehr</B> kap1.txt: sehr wichtig...'. kap1.txt: Ausdrücken denkt; so daß Sie sie anwenden kap1.txt: sehr verschieden sein. Ein Programm kennt kap1.txt: kennt vielleicht dieses oder jenes Zeichen nicht,
»Verdoppelte Wörter« in modernem Perl
$/ = ".\n"; 1 # ein spezieller Einlese-Modus
while (<>) 2
{
next unless s 3
{ # (Regex beginnt hier)
### Ein Wort erkennen:
\b # Wortanfang....
( [a-z]+ ) # Wort, setzt $1 (und \1).
### Whitespace und/oder <TAGS> dazwischen
( # Zwischenraum in $2 speichern.
( # (drittes Klammerpaar gruppiert nur)
\s # Whitespace (inkl. Newline, gut in diesem Fall).
| # -oder-
<[^>]+> # etwas wie <TAG>.
)+ # Mindestens eins davon, aber auch mehr.
)
### Und das gleiche Wort nochmal erkennen:
(\1\b) # \b, damit nicht Wortteile erkannt werden. Setzt $4.
# (Regex endet hier)
}
# Ersatz-String folgt hier, mit den Modifiern /i, /g und /x
"\e[7m$1\e[m$2\e[7m$4\e[m"igx; 4
s/^([^\e]*\n)+//mg; 5 # Nicht markierte Zeilen löschen.
s/^/$ARGV: /mg; 6 # Jeder Zeile Dateinamen voranstellen.
print;
}
Mit dem next unless ² vor der Substitution geht Perl zum nächsten Zyklus der while-Schlaufe, wenn der reguläre Ausdruck in der Substitution nichts gefunden hat und die Substitution keine Wirkung hatte. Wenn keine verdoppelten Wörter gefunden werden, brauchen wir uns nicht weiter mit dem eingelesenen String zu befassen und lesen den nächsten.
Wenn wir die Klammerung der Regex genau anschauen, sehen wir, daß "$1$2$4" alle Zeichen enthält, die auf die Regex gepaßt haben. Damit ist die ganze Substitution bis auf das Hinzufügen der Escape-Sequenzen eigentlich nur ein (langsames) NOP, eine »Nicht-Operation«.
Wir wissen, daß $1 das gleiche enthält wie $4 (das ist ja der Sinn des Programms!), also könnte man im Ersatz-String zweimal $1 benutzen und auf das vierte Klammerpaar verzichten. Aber die zwei können sich bezüglich Groß- und Kleinschreibung unterscheiden, daher benutze ich beide.
^([^\e]*\n)+
findet Sequenzen von Zeichen außer ESC, die mit
einem Newline enden. Die Substitution mit dieser Regex entfernt diese Sequenzen.
Als Resultat bleiben die logischen Zeilen übrig, die mindestens ein
ESC-Zeichen enthalten; mithin die
logischen Zeilen, die verdoppelte Wörter enthalten.
¹ Diese Variable heißt $_ (jawohl, auch das ist eine Variable). $_ wird bei vielen Operatoren und Funktionen per Voreinstellung benutzt, wenn nicht explizit eine andere Variable angegeben wird.
² unless bedeutet in Perl dasselbe wie if !, läßt sich aber (für Englisch sprechende Mitmenschen) viel leichter lesen. (Anm. d. Ü.)
³ Hier wird angenommen, daß die Datei nicht schon von vornherein ASCII-ESC-Zeichen enthält. Wenn dem nicht so ist, werden Zeilen ohne doppelte Wörter fälschlich ausgegeben.