Aufteilen in mehrere reguläre Ausdrücke

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

In bestimmten Fällen kann es große Vorteile bringen, statt einer großen, komplizierten Regex viele kleine anzuwenden. Wenn beispielsweise ein Text auf die Monatsnamen untersucht werden soll, ist es möglicherweise schneller, separat auf ˹Januar˼, ˹Februar˼, ˹März˼ usw. zu testen, als eine große Alternation ˹Januar|Februar|März|...˼ zu verwenden. Im letzteren Fall gibt es keinen literalen String, der in allen Alternativen vorkommt; die entsprechende Optimierung (siehe Optimierung mit literalen Strings mitten im Text) kann daher nicht angewendet werden. Bei der einen Regex mit allen Monatsnamen ist der Suchvorgang an jedem einzelnen Punkt im Suchstring ziemlich langsam.

In diesem Zusammenhang ist mir gerade etwa zu der Zeit, als ich diesen Abschnitt geschrieben hatte, ein Problem dieses Typs untergekommen. Ich arbeitete mit einem Perl-Modul zur Datenbankanbindung und hatte bemerkt, dass mein Client-Programm fälschlicherweise Anfragen mit Strings wie ›HASH(0x80f60ac)‹ statt mit dem Inhalt dieses Hash-Elements abschickte. Ich wollte das Modul so erweitern, dass diese Art von falschen Daten erkannt und eine entsprechende Fehlermeldung ausgegeben würde. Dafür wollte ich einen regulären Ausdruck der Art ˹\b(?:SCALAR|ARRAY|...|HASH)\(0x[0-9a-fA-F]+\)˼ einsetzen.

In der gegebenen Situation war Effizienz sehr wichtig. Ist diese Regex schnell? Perl hat einen Debugging-Modus für reguläre Ausdrücke, mit dem man einiges über die verwendeten Optimierungen herausfinden kann (siehe dazu Debugging-Informationen zu regulären Ausdrücken). Mit diesem wollte ich prüfen, ob die Optimierung Suche nach einzelnen Zeichen oder festen Substrings angewendet würde – eine gute Regex-Implementation sollte eigentlich herausfinden, dass in jedem Treffer ›(0x‹ vorkommen muss. Ich wusste außerdem, dass in meinen Daten so gut wie nie ein ›(0x‹ auftritt, also sollte diese Optimierung fast immer zu einem wirklichen Treffer führen. Leider erkannte Perl in diesem Fall die Möglichkeit für die Optimierung nicht, die Regex musste so bei jedem Zeichen alle Alternativen durchgehen. Das war unakzeptabel langsam.

Da ich ja mitten in den Untersuchungen zu Optimierungsmethoden steckte, war es schon eine Herausforderung, diese Regex schneller zu machen. Ein Versuch war das ziemlich komplizierte ˹\(0x(?<=(?:SCALAR|...|HASH)\(0x)[0-9a-fA-F]+\)˼. Die Idee dabei ist, dass zuerst nach ˹\(0x˼ gesucht wird, und mit dem positiven Lookbehind wird danach sichergestellt, dass eines der Schlüsselwörter aus der Alternation gerade davor auftritt. Mit diesen Verrenkungen wollte ich die Regex-Maschine dazu bringen, als Erstes nach dem literalen String ˹\(0x˼ zu suchen und so effizienter vorzugehen. Dabei könnte die Optimierung Suche nach einzelnen Zeichen oder festen Substrings und auch die »Erstes Zeichen«-Optimierung angewendet werden (siehe »Erstes Zeichen«-Optimierung). Ich denke, diese Regex wäre in der Tat sehr schnell – aber leider unterstützt Perl nur das Lookbehind mit Ausdrücken fester Länge (siehe Lookahead; Lookbehind), ich musste also wieder bei Null anfangen.

Wenn Perl jedoch den Suchstring nicht von selbst auf das literale ˹\(0x˼ überprüft, könnte ich das ja von Hand erledigen:

if ($data =~ m/\(0x/
    and
    $data =~ m/(?:SCALAR|ARRAY|...|HASH)\(0x[0-9a-fA-F]+\)/)
{
   # Warnung über ungültige Daten ausgeben ...
}

Der Vergleich mit ˹\(0x˼ schließt fast alle Zeilen aus, und nur die wenigen Zeilen, die diesen String enthalten, werden mit der großen, relativ langsamen Regex weiter überprüft. Das führte zum einen zu einer sehr schnellen Lösung, die zum anderen auch sehr gut lesbar ist. (Anmerkung: Sie können das selbst überprüfen. Das fragliche Modul heißt DBIx::DWIW und ist auf dem CPAN zu finden. Es ist ein Modul, mit dem man auf einfache Art auf eine MySQL-Datenbank zugreifen kann. Jeremy Zawodny und ich haben das Modul bei Yahoo! entwickelt.)

  

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