Prozeduraler und objektorientierter Ansatz

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

Der prozedurale und der objektorientierte Ansatz haben einiges gemeinsam. Bei beiden ist die Regex-Funktionalität nicht direkt in der Sprache verankert, sondern wird mit normalen Funktionen (beim prozeduralen Ansatz) oder mit Konstruktoren und Methoden (objektorientiert) implementiert. Die Operanden sind nicht wirklich reguläre Ausdrücke, sondern normale Strings, die von den Funktionen, Konstruktoren und Methoden als reguläre Ausdrücke interpretiert werden.

Im Folgenden werden Beispiele in Java, VB.NET, PHP und Python vorgestellt.

Gebrauch von regulären Ausdrücken in Java

Betrachten wir das Äquivalent zum »Subject«-Beispiel in Java anhand des java.util.regex-Packages von Sun. (Java wird eingehend unter Java behandelt.)

import java.util.regex.*; // Auf einfache Art alle Regex-Klassen einbinden.
.
.
.
Pattern r = Pattern.compile("^Subject: (.*)", Pattern.CASE_INSENSITIVE);     
[= Schritt 1]
Matcher m = r.matcher(zeile);   [= Schritt 2]
if (m.find()) {                               [= Schritt 3]
 subject = m.group(1);                 [= Schritt 4]
}

Wieder sind die Variablennamen kursiv, die Elemente, die mit regulären Ausdrücken zu tun haben, fett und die Regex selbst unterstrichen dargestellt. Um genau zu sein: Das Unterstrichene ist ein ganz normaler literaler String, der später als Regex interpretiert wird.

Das Beispiel illustriert den objektorientierten Ansatz, bei dem die Regex-Funktionalität in zwei Klassen aus dem java.util.regex-Package von Sun ausgeführt ist: Pattern und Matcher. Der Ablauf ist der folgende:

Schritt 1: Der reguläre Ausdruck wird eingelesen und in eine interne Form kompiliert, die Groß- und Kleinschreibung ignoriert. Das Resultat ist ein Pattern-Objekt.

Schritt 2: Der Suchtext wird mit dem Pattern verbunden und ergibt ein Matcher-Objekt.

Schritt 3: Die Regex wird auf den Text angewendet, und es wird überprüft, ob ein Treffer gefunden wurde.

Schritt 4: Wenn es einen Treffer gab, wird der vom ersten Klammerpaar gefundene Text ausgewertet und in einer Variablen abgespeichert.

Diese oder ähnliche Schritte müssen von jedem Programm, das mit regulären Ausdrücken arbeitet, ausgeführt werden. In Perl werden viele Details implizit hinter den Kulissen erledigt, in dieser Java-Implementation ist normalerweise alles explizit sichtbar.

Ein prozedurales Beispiel

Im Regex-Package von Sun gibt es immerhin auch ein paar prozedural orientierte Funktionen, die diese Details verbergen. Statt erst ein Regex-Objekt zu erzeugen und die Objekt-Methoden anzuwenden, verwenden diese Funktionen intern temporäre Objekte zum einmaligen Gebrauch. Hier sehen Sie ein Beispiel mit der Funktion Pattern.matches(...):

if (! Pattern.matches("\\s*", zeile))
{
    // ... Zeile ist nicht leer ...
}

Bei dieser Funktion wird die Regex implizit in ein ˹^...$˼ eingepackt. Sie liefert einen Wahrheitswert zurück, der besagt, ob die Mustersuche im String erfolgreich war. In vielen Regex-Packages wird – wie hier beim Package von Sun – sowohl die prozedurale als auch die objektorientierte Schnittstelle angeboten. Es gibt Unterschiede im Gebrauch (die prozedurale Schnittstelle ist meist einfacher für simple Dinge und schwieriger im Gebrauch bei komplexen Problemen), bei der Funktionalität (prozedurale Schnittstellen sind meist weniger reichhaltig ausgestattet) und bei der Effizienz (hier hängt es oft von der besonderen Situation ab, welcher Ansatz effizienter ist – mehr dazu unter Die Kunst, reguläre Ausdrücke zu schreiben).

Ab und zu hat Sun die Regex-Funktionalität in andere Teile der Sprache integriert. Zum Beispiel kann man das vorherige Beispiel auch mit der matches-Methode der String-Klasse formulieren:

if (! zeile.matches("\\s*", ))
{
    // ... Zeile ist nicht leer ...
}

Auch das ist nicht so effizient wie die ausformulierte objektorientierte Methode und sollte deshalb so nicht in einer zeitkritischen Schleife benutzt werden, aber für Anwendungen mit geringen Anforderungen ist es so viel praktischer.

Reguläre Ausdrücke in VB und anderen .NET-Sprachen

Im Grunde erfüllen alle Regex-Implementationen die gleichen Aufgaben, und doch unterscheiden sie sich darin, wie viele von diesen Aufgaben und Arbeiten für den Programmierer sichtbar sind. Es gibt diese Unterschiede sogar zwischen Regex-Implementationen, die den gleichen Ansatz verfolgen. Hier folgt das »Subject«-Beispiel in VB.NET (.NET wird unter .NET ausführlich behandelt):

Imports System.Text.RegularExpressions   ' Regex-Klassen einbinden.
    .
    . 
    .
Dim R as Regex = New Regex("^Subject: (.*)", RegexOptions.IgnoreCase)
Dim M as Match = R.Match(zeile)

If M.Success
    subject = M.Groups(1).Value
End If

Insgesamt ähnelt das dem Beispiel in Java doch sehr, in .NET werden allerdings die Schritte 1 und 3 zu einem, und in Schritt 4 wird das zusätzliche Value benötigt. Warum diese Unterschiede? Keiner der beiden Ansätze ist »besser« als der andere – sie wurden eben von den Entwicklern so geschrieben, wie diese es zu einem bestimmten Zeitpunkt für gut hielten.

Auch bei .NET gibt es Funktionen für den prozeduralen Ansatz. Dieses Programmstück testet auf Leerzeilen:

If Not Regex.IsMatch(Zeile, "^\s*$") Then
   ' ... Zeile ist nicht leer ...
End If

Im Gegensatz zur Pattern.matches-Funktion von Sun, bei der ein ˹^...$˼ impliziert wird, ist die Funktion bei Microsoft genereller. Es ist eigentlich nur eine praktische Verpackung der eigentlichen Regex-Objekte; der Programmierer verliert nicht viel und spart sich damit Tipparbeit und ein paar Variablen, die ja auch deklariert werden müssen.

Reguläre Ausdrücke in PHP

Hier folgt eine Version des ˹Subject˼-Beispiels mit dem prozedural orientierten preg-Funktionen von PHP (PHP wird unter PHP ausführlich behandelt):

if (preg_match('/^Subject: (.*)/i', $zeile, $treffer))
      $subject = $treffer[1]

Reguläre Ausdrücke in Python

Als letzte Variante stellen wir das »Subject«-Beispiel in Python vor, wo ein objektorientierter Ansatz verfolgt wird:

import re;
    .
    . 
    .
M = R.search(zeile)
if M:
    subject = M.group(1)

Auch das sieht sehr ähnlich aus wie die vorhergehenden Beispiele.

Warum verschiedene Ansätze?

Warum wird es in der einen Sprache so gemacht, warum in der nächsten anders? Manche Dinge ergeben sich aus der Struktur und der »Philosophie« der Sprache, aber oft sind es einfach die Launen und die Fähigkeiten des Programmierers, der ein bestimmtes Package entwickelt. Für Java gibt es beispielsweise eine Reihe von unterschiedlichen Regex-Packages, die von Leuten entwickelt wurden, die alle das Gleiche wollten: reguläre Ausdrücke, die Sun damals nicht im Angebot hatte. Jedes Package hat seine Stärken und Schwächen, aber alle haben die gleiche Grundfunktionalität; interessanterweise unterscheiden sich doch alle voneinander und auch von der Implementation, die Sun schließlich in Java integriert hat.

Ein anderes Beispiel für diese Vielfalt ist PHP, das gleich drei vollständig unabhängige Regex-Implementationen enthält. Das kommt einfach von daher, dass verschiedene PHP-Entwickler zu unterschiedlichen Zeiten mit der vorhandenen Funktionalität unzufrieden waren und dem PHP-Kern jeweils ein neues Paket hinzugefügt haben (von diesen gilt das »preg«-Paket allgemein als das beste, und nur dieses wird in diesem Buch behandelt).

  

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