s.oliver: Lookarounds in unserem Beispiel

Beitrag lesen

Servus,

also, noch mal zu den Lookarounds.

Es gibt zwei Lookaheads, welche sich nach rechts(*) orientieren:
positiv - (?=bla)
negativ - (?!bla)

und zwei Lookbehinds, welche sich nach links(*) orientieren
positiv - (?<=bla)
negativ - (?<!bla)

(*) entsprechend unserem Schriftsystem ("von links oben nach rechts
unten"), orientiert sich der "Blick voraus" nach rechts und der
"Blick zurück" nach links

Dabei wird von den Lookarounds erwartet, dass die enthaltene Zeichenkette
(bla) in der jeweiligen Richtung direkt angrenzend an den Lookaround vorkommt
(positiv) oder nicht vorkommt (negativ). Wird die Erwartung erfüllt, geben
sie grünes Licht für den aktuellen Suchvorgang. Bitte beachten: Lookbehinds
dürfen im Gegensatz zu Lookaheads nur auf Zeichenketten mit fester Länge
testen - die Benutzung von Quantifizierern wie .*, .*?, .+, .{x,} etc. ist
deshalb beispielsweise nicht gestattet. Das ist auch ein Grund, weshalb wir
für unsere Negation lediglich den Lookahead benutzen.

So, was macht eigentlich unser Ausdruck /"((?![^"]*html[^"]*)[^"]*[^" ][^"]*)"/?
Schlüsseln wir das Ganze mal auf - die Zeilen 02 bis 06 gehören zum Lookahead.

"                #01 finde das Zeichen "
    (?!          #02 Schaue voraus; stelle fest, ob die folgende Kette nicht vorkommt
        [^"]*    #03   jedwedes Zeichen ausser " [1]
        html     #04   die Zeichenkette html
        [^"]*    #05   jedwedes Zeichen ausser " [1]
    )            #06 Ende des Ausblicks
    [^"]*        #07 finde jedwedes Zeichen ausser " [1]
    [^" ]        #08 finde ein Zeichen, welches weder ein '"' noch ein ' ' ist [2]
    [^"]*        #09 finde jedwedes Zeichen ausser " [1]
"                #10 finde das Zeichen "

[1] Findet so viele Charaktere wie möglich (0 bis n)
[2] Damit soll ein leeres Ergebnis vermieden werden, bzw eines,
    welches nur Leerzeichen enthält.

Die RegEx-Engine setzt in Zeile 02 zum Ausblick nach rechts an, und prüft,
ob der gesamte String den wir erfassen wollen, die Zeichenkette html _an
einer beliebigen Stelle_ enthält. Wir suchen sozusagen zwei Mal nach der
gesamten Zeichenkette, die für uns interessant ist, einmal im Lookahead
und einmal in unserer eigentlichen Zeichensuche. Entsprechend der Zeilen
03 und 05 darf jede beliebige Anzahl von Zeichen ausser " vor und nach
dem html auftreten - "html_bla" ist von unserem Muster also ebenso
betroffen, wie "bla_html_bla", "html_bla" und "html". Du kannst für Dich
mal testen, wie Du den Lookahead umstellen müsstest, damit nur jeweils
eine dieser Möglichkeiten ausgeschlossen wird.

Sobald die Engine den Lookahead mit Zeile 06 komplett angewandt hat,
wird das Ergebnis ermittelt - wurde html an keiner beliebigen Stelle
des Strings gefunden, ist das negative Lookahead erfolgreich. Nur im
Erfolgsfall wird jetzt ab Zeile 07 unser Suchmuster erneut auf den
String angewandt, und die einzelnen Zeichen werden von der Engine
"gefressen" (d.h. intern in das Ergebnis einbezogen, wobei der Zeiger
jeweils auf die Position nach dem gefressenen Zeichen wandert).

Wenn wir wie hier auf das Nichtvorhandensein an einer beliebigen Stelle
testen, muss der Lookahead an den Anfang des Suchmusters, da der Inhalt
des Lookaheads ebenfalls das gesamte Suchmuster testet (ansonsten geht
die Wirkung des Lookaheads verloren) - sobald wir irgendeine Zeichenkette
vor dem Lookahead suchen lassen, ist die Kontrollwirkung des Lookaheads
ausgehebelt, welche in unserem Beispiel ja lautet: "Starte die Ergebnis-
findung nur, wenn ich vorher html nicht finden konnte".

Grüsse