Lookahead und Lookbehind - Zwei unbekannte aber hilfreiche RegEx-Funktionen

    • VB.NET

      Lookahead und Lookbehind - Zwei unbekannte aber hilfreiche RegEx-Funktionen

      Hallo zu diesem kleinen Tutorial.

      Zuerst einmal möchte ich anmerken, dieses Tutorial setzt allgemeines Wissen über RegEx voraus. Dies könnt ihr euch in RegEx Tutorial - blutige Anfänger und Fortgeschrittene aneignen (ein hervorragender Artikel, ich selbst habe RegEx dort gelernt).
      Als ich neulich mal wieder dort vorbeigeschaut habe, ist mir aufgefallen, dass die Liste der Befehle dort zwar sehr ausführlich, aber nicht ganz vollständig ist. Es fehlen zwei meiner Meinung nach außerst nützliche Funktionen, die ich nun schon öfter gut gebrauchen konnte. Diese sind Lookahead und Lookbehind.

      Aber mal der Reihe nach.
      Angenommen wir suchen alle "a" in einem String, auf die ein "b" folgt (die Buchstaben können natürlich durch jedes beliebige Pattern ausgetauscht werden). Normalerweise würde man da jetzt so etwas machen:
      "ab"
      Fertig. Das Problem hierbei ist nur, dass man so in den Matches auch das "b" erhält, das ja eigentlich nicht direkt gesucht ist.
      Auch das kann man bewerkstelligen, indem man Gruppen verwendet:
      "(?<value>a)b"
      So können wir bei jedem Match die Gruppe "value" abfragen, die dann nur "a" enthält, soweit so gut.


      Nun ist es aber so, dass diese Gruppen bei komplizierteren Pattern nicht mehr gerade übersichtlich sind und sie das Pattern auch um einiges in die Länge ziehen.
      Hier kommen nun die von mir oben angesprochenen RegEx-Funktionen ins Spiel.
      Das grundlegende Prinzip hinter Lookahead und Lookbehind ist, dass man damit prüft, ob ein bestimmtes Pattern matcht, der jeweilige Teil aber nicht am Ende in den Match mit aufgenommen wird.

      Die Syntax für den Lookahead sieht so aus:
      (?=x)
      Wobei das "x" durch jedes beliebige gültige RegEx-Pattern ersetzt werden kann.
      Und für den Lookbehind sieht es ähnlich aus:
      (?<=x)

      Angewendet auf unser Beispiel würde das dann so aussehen:
      "a(?=b)
      Das Pattern, nach dem wir nur suchen aber es nicht mit einbeziehen wollen, ist "b", also setzen wir es an der Stelle von "x" ein. Wenn wir hiermit nun eine Abfrage auf Matches durchführen, dann erhalten wir nur "a"s, auf die ein "b" folgt, allerdings sind diese direkt die Matches selbst, wir müssen also keine Gruppen o.ä. mehr abfragen.

      Genau so läuft es umgekehrt ab. Wollten wir alle "b"s, die auf ein "a" folgen, dann würde dies so aussehen:
      "(?<=a)b"
      Beachtet hier, dass wird den Lookbehind statt dem Lookahead brauchen, denn wir suchen ja nach etwas, dass vor dem gesuchten Match steht, nicht danach.


      Diese Vorgehensweise hat noch einen weiteren Vorteil.
      Gegeben sei, wir suchen nach allen Zahlen, vor denen und nach denen ein Leerzeichen steht (die also frei stehen), in diesem String:
      " asfh 12 23 ijdsaf 234 88 217 kfdash 98 "
      Dann würde das Pattern ohne Lookahead/Lookbehind so aussehen:
      " \d+ "
      Das Problem dabei wäre aber, dass wir nur diese Ausgabe erhalten würden:
      { " 12 ", " 234 ", " 217 ", " 98 " }
      Das liegt daran, dass das Leerzeichen Teil des Rückgabepatterns ist, und damit z.B. das Leerzeichen nach "12" nicht mehr für "23" verfügbar ist.

      Anders ist es mit Lookahead und Lookbehind, diese markieren das Leerzeichen nicht als "bereits benutzt", wodurch es auch für den nachfolgenden Match gültig ist.
      Das Pattern sähe so aus:
      (?<= )\d+(?= )
      Und die Ausgabe, die wir damit erhalten würden, wäre:
      { "12", "23", "234", "88", "217", "98" }
      Also genau das, was wir gesucht haben. Man beachte auch, dass hier im Vergleich zu vorher die Leerzeichen nicht mir in der Ausgabe enthalten sind.


      Man kann bei beiden befehlen das "=" durch ein "!" ersetzen. Damit prüft man dann auf das genaue Gegenteil, also darauf, ob davor/danach nicht das angegebene Pattern auftaucht.
      "a(?!b)" z.B. würde nur "a"s finden, auf die kein "b" folgt.


      Habt ihr noch Fragen? Nur her damit.