Ungewollte Treffer vermeiden

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

Es wird leicht übersehen, was passiert, wenn der untersuchte Text nicht so aussieht, wie man es sich vorgestellt hat. Nehmen wir an, Sie schreiben ein Filter-Programm, das nackten Text in HTML übersetzen soll. Mehrere Bindestriche sollen dabei durch ein <HR>-Tag ersetzt werden, das einem Balken quer über die Seite entspricht (»HR« steht für »horizontal rule«, waagrechter Strich). Mit der Substitution s/-*/<HR>/ werden tatsächlich Bindestrich-Sequenzen durch Balken ersetzt, aber nur am Beginn einer Zeile. Erstaunt? Nun, s/-*/<HR>/ stellt jeder Zeile ein <HR> voran, ob nun Bindestriche vorhanden sind oder nicht.

Noch einmal: Jedes Element der Regex, das nicht zwingend vorgeschrieben ist, passt immer. Wenn ˹-*˼ auf einen String angesetzt wird, passt der Ausdruck auf einen oder mehrere Bindestriche am Anfang des Strings. Wenn da aber kein Bindestrich ist, passt der Ausdruck auch, er passt auf das »Garnichts« am Anfang des Strings. Darum geht es ja beim Stern.

Ein ähnliches Beispiel habe ich im Buch eines bekannten Autors gefunden. Darin wird eine Regex entwickelt, die Zahlen erkennen soll, auch Fließkommazahlen. Zulässige Zahlen beginnen dabei mit einem optionalen Minuszeichen, gefolgt von einer beliebigen Anzahl Ziffern, einem optionalen Dezimalpunkt und beliebig vielen Nachkommastellen. Seine Regex ist ˹-?[0-9]*\.?[0-9]*˼.

Tatsächlich erkennt dies Texte wie 1, -272.37, 129238843., .191919 und sogar so etwas wie -.0. Das ist ja auch, was erwartet wird.

Passt das auch auf ›DieserTestenthältkeineZiffern‹, ›Nixda‹ oder gar auf den leeren String? Betrachten Sie die Regex genau – alles darin ist optional. Wenn eine Ziffer vorkommt und wenn diese am Anfang des Strings vorkommt, dann wird sie erkannt, aber das ist alles gar nicht vorgeschrieben! Diese Regex passt auf alle drei der nicht-numerischen Texte, weil sie auf das »Garnichts« am Anfang jedes Strings passt. Die Regex passt auch auf dieses Garnichts am Anfang von ›Nummer123‹, weil das Garnichts früher passt als die Zahl 123.

Also: Es ist wichtig zu sagen, was man wirklich meint. Eine Fließkommazahl muss mindestens eine Ziffer enthalten, sonst ist es keine Zahl. Beim Aufbau einer besseren Regex fordern wir zunächst, dass vor dem Dezimalpunkt mindestens eine Ziffer erscheinen muss. Dazu verwenden wir das Pluszeichen: ˹-?[0-9]+˼. Im Unterausdruck für den fakultativen Nachkomma-Teil ist es wichtig, dass Ziffern nur vorkommen dürfen, wenn auch ein Punkt vorhanden ist. Wenn wir naiverweise einfach ˹\.?[0-9]*˼ nehmen, dann kann ˹[0-9]*˼ passen, ohne dass zuvor ein Punkt erkannt wurde.

Der richtige Weg ist, genau zu sagen, was wir eigentlich wollen. Ein Dezimalpunkt (und mögliche Ziffern danach) ist optional: ˹(\.[0-9]*). Hier quantifiziert (oder: beschreibt) das Fragezeichen nicht mehr nur den Dezimalpunkt, sondern die ganze Kombination aus Dezimalpunkt und Nachkommastellen. Innerhalb dieser Kombination ist der Punkt vorgeschrieben; wenn er fehlt, wird die Maschine nie zu ˹[0-9]*˼ vordringen.

Zusammengesetzt lautet der Ausdruck ˹-?[0-9]+(\.[0-9]*). Das erkennt Dinge wie ›.007‹ nicht, weil unsere Regex mindestens eine Ziffer vor dem Dezimalpunkt verlangt. Wenn wir die linke Seite so ändern, dass auch null Ziffern zugelassen sind, müssen wir entsprechend mindestens eine Nachkommaziffer fordern – wir müssen im gesamten Ausdruck mindestens eine Ziffer haben (sonst hätten wir das Problem, von dem wir ausgegangen sind).

Die hier verfolgte Lösung fügt eine Alternative hinzu, die den bisher unzulässigen Fall abdeckt: ˹-?[0-9]+(\.[0-9]*)?|-?\.[0-9]+˼. Das erlaubt nun auch nur einen Dezimalpunkt, gefolgt von einer Ziffer oder mehreren. Und ein optionales Minus davor, so etwas vergisst man leicht. Details, Details. Man kann die Alternation auch einklammern und das ˹-?˼ nur einmal davor angeben: ˹-?([0-9]+(\.[0-9]*)?|\.[0-9]+)˼.

Das ist zweifellos ein Fortschritt gegenüber dem Original, aber auch dieser Ausdruck kann noch auf 2003.04.12‹ passen. Oft wird angenommen, dass die Regex auf ganz bestimmte Daten angewandt wird oder dass sie als Teil einer größeren Regex gebraucht wird, die vor und nach der Zahl bestimmte Zeichen nicht zulässt. Unsere Fließkommazahl-Regex funktioniert nur dann, wenn sie in eine größere Regex eingebaut wird, zum Beispiel in ˹^...$˼ oder auch in ˹Wert\s*=\s*...$˼.

  

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