Home ORDIX AG             Dienstleistung             Trainingsshop    Kunden / Referenzen Aktuelles    Kontakt
Home  Pfeil  ORDIX News  Pfeil  4/2002
suche: 

ORDIX News Archiv

Das IT-Magazin der ORDIX AG mit Fachbeiträgen zu Datenbanken, Unix und Java/XML.

Neue Reihe Java 1.4 Neuheiten:

Reguläre Ausdrücke in Java

^.*[0-9]*\$??

In den nächsten Ausgaben der ORDIX News werden jeweils Kernthemen der neuen Java 1.4 API in den Fokus gerückt und präsentiert. Dieses Mal starten wir mit Regulären Ausdrücken.

"Reguläre Ausdrücke" sind ein wohlbekanntes Thema der Informatik, welches in Java in der aktuellen Version 1.4 (genauer "Java 2 SE 1.4.1") dem Standard Repertoire hinzugefügt wurde. Damit kommen die SUN Entwickler einer dringend herbeigesehnten Anforderung nach, nämlich effiziente Analysemöglichkeiten von Zeichenketten und Texten stärker in der Java Technologie zu unterstützen.

Reguläre Ausdrücke

Das Thema Ein-/Ausgabe hat in Java 1.4 den größten Stellenwert bekommen, und zu diesem Komplex gibt es den Hauptteil an Neuerungen (siehe auch ORDIX News 4/2001). Die Möglichkeit zur Verarbeitung von Regulären Ausdrücken stellt dabei ein Teilpaket dar.

Vereinfacht ausgedrückt dienen Reguläre Ausdrücke dazu, Suchmuster zu definieren, und mit diesen in Texten zu suchen und zu ersetzen. Dabei liegt eine Hauptstärke von Regulären Ausdrücken in der mächtigen Ausdrucksmöglichkeit zur Suchmusterdefinition.

Reguläre Ausdrücke kommen in vielen Anwendungsgebieten vor und dürften vielen Lesern bekannt sein durch Unix Tools und Editoren (grep, find, vi, sed, awk, ...), durch andere Programmier- bzw. Skriptsprachen oder durch IDEs.

Ein bekannter Vertreter im Sprachenumfeld ist Perl (Practical Extraction and Reporting Language), das man, wie der Name schon sagt, häufig zum Analysieren und zum "Extrahieren" von Informationen aus Textbestandteilen verwendet.

Zunächst sehen wir uns die Java API zum Thema etwas genauer an und gehen auf die Verwendung der Klassen und deren wichtigste Methoden ein.

Anschließend findet noch eine kurze Darstellung der Ausdrucksmöglichkeiten Regulärer Ausdrücke statt, was weitgehend den von Perl bekannten Möglichkeiten entspricht.

Pattern und Matcher

Die beiden Hauptakteure bei den Regulären Ausdrücken in Java sind das Pattern und der Matcher. Die zugehörigen, gleichnamigen Klassen befinden sich im Package java.util.regexp und bilden schon die gesamte Java API zu diesem Bereich, was durchaus als Zeichen der Schlankheit und Eleganz zu bewerten ist.

Pattern Objekte kapseln Reguläre Ausdrücke, die über Zeichenketten definiert und für eine effiziente Verarbeitung intern kompiliert werden. Daher ist es nicht möglich, Instanzen dieser Klasse mittels new zu konstruieren, sondern solche mit der Klassenmethode compile von der Klasse selbst liefern zu lassen.

Pattern p = Pattern.compile("ein");

Die tatsächliche Arbeit und die Aktionen führt nun der Matcher durch, häufig auch als "Engine" bezeichnet. Einen Matcher bekommen wir vom Pattern Objekt unter Angabe einer zu analysierenden Zeichenkette.

Matcher m =
p.matcher("ein netter Test, wenn auch" + "ein sinnloser Test");

Matcher können auf vielfältige Weise eingesetzt werden und sie führen während einer Analyse die interne Verwaltung durch. D. h. von ihnen kann abgefragt werden, ob und welche Teilzeichenkette gefunden wurde, wo sich die relevanten Positionen im Analysetext befinden, wie eine Ersetzung stattfinden kann, usw.

Pattern hingegen haben keinen sich verändernden Zustand, sie stehen für einen Regulären Ausdruck, der auch von verschiedenen Matcher Instanzen parallel genutzt werden kann.

Aber beginnen wir mit der Arbeit.

Erste Frage: Beginnt ein Input Text mit einem Suchmuster (hier das Wort "ein")?

boolean b = m.lookingAt();

Zweite Frage: Wie oft kommt das Suchmuster (hier das Wort "Test") im Input Text vor?

Pattern p = Pattern.compile("Test");

Matcher m =
p.matcher("ein netter Test, wenn auch" + "ein sinnloser Test");

int i = 0;

while (m.find()) i++;

Dritte Frage: Welches Suchmuster soll bei jedem Vorkommen durch einen spezifizierten Ersatztext ersetzt werden? Wie wird das Ersetzen realisiert?

Im folgenden Beispiel-Quelltext wird gezeigt, wie man jedes Vorkommen des Suchmusters "Test" durch "Beitrag" ersetzt.

Pattern p = Pattern.compile("Test");

Matcher m =
p.matcher( "ein netter Test, wenn auch" + "ein sinnloser Test");

StringBuffer sb = new StringBuffer();

boolean result = m.find();

while(result) {
m.appendReplacement(sb, "Beitrag");
result = m.find();
}
// Häng den Rest an
m.appendTail(sb);
System.out.println(sb.toString());

Die ersten beiden Fragestellungen und Codestücke sind trivial und selbsterklärend. Von der dritten Aufgabe wollen wir einige typische, neue Dinge beleuchten. Die zu analysierende Zeichenkette oder Input Text (Argument der matcher() Methode) ist vom Typ CharSequence, einem neuen Interface in Java 1.4, das für eine "lesbare Sequenz von Zeichen" steht und aktuell von den Klassen String, StringBuffer, CharBuffer implementiert wird. Hier verwenden wir einen String.

Die Methode find() sucht das nächste Vorkommen des Suchmusters. Dieser Vorgang wird auch als "Match" bezeichnet. Er merkt sich die Begrenzung des Vorkommens innerhalb des Input Textes. Diese Begrenzungen können mit den Methoden start() und end() abgefragt werden. Das Matcher Objekt hält intern die Information, wo das letzte find() erfolgreich war. Der Rückgabewert vom Typ boolean gibt Auskunft, ob die Suche überhaupt erfolgreich war.

Die Ersetzung geschieht nun mit der Methode appendReplacement(), der ein StringBuffer und der Ersetzungstext mitgegeben werden.

Wozu brauchen wir plötzlich einen StringBuffer und warum benutzen wir nicht den Eingabe String? Weil String Instanzen nicht modifizierbare Zeichenketten in Java sind, und genau das können wir hier gar nicht gebrauchen. An den Stringbuffer wird nun die Zeichenkette vom Ende des letzten "Match" bis inklusive des aktuellen "Match" angehängt, der allerdings mit dem Ersetzungstext ausgetauscht wird. Was übrig bleibt, ist der Rest, in dem das Suchmuster nicht mehr vorkommt, und den bekommen wir mit der Methode appendTail() geliefert. Die obige Ausgabe liefert also erwartungsgemäß:

> ein netter Beitrag, wenn auch ein sinnloser Beitrag

Ein weiterer Vertreter der Suchmethoden aus der Matcher Klasse ist die Methode matches(), die ähnlich wie lookingAt() arbeitet, jedoch die vollständige Übereinstimmung des Suchmusters mit dem Input Text untersucht.

Wie drücke ich mich aus?

Die Mächtigkeit der API kommt allerdings erst durch die Ausdrucksmöglichkeiten zustande, mit denen Suchmuster erzeugt, also Pattern kompiliert werden können.

Die Definition der Regulären Ausdrücke in Perl 5 war die Basis für die neue API in Java 1.4, wobei einige Spezialkonstrukte aus Perl herausfielen, gewisse andere jedoch hinzugekommen sind, die stark mit der Verwendung von Unicode in Java zusammenhängen. In Abb. 1 finden Sie einen kleinen Auszug der Java Definitionen, wie Reguläre Ausdrücke aufgebaut werden können und welche Konstrukte für deren Initialisierung (bzw. Kompilierung) zu benutzen sind. Die vollständige Liste würde diesen Artikel sprengen, kann aber jederzeit in der API Dokumentation zur Pattern Klasse nachgelesen werden.

In Abb. 2 sehen Sie nun ein etwas komplexeres Beispiel zum Einsatz von Regulären Ausdrücken, in dem wir ansatzweise mit den oben beschriebenen Ausdrucksmöglichkeiten arbeiten. Es sollen aus einer vorgegebenen Java Quellcode Datei alle Kommentare beginnend mit "//" gefunden und ausgegeben werden, in denen zusätzlich das Kürzel "ha:" gefolgt von einem oder mehreren Leerzeichen und einem Datum (Format: JJ/MM/TT bzw. JJJJ/MM/TT) vorkommt.

Wendet man das Programm auf sich selbst an, so erhält man folgende Ausgabe:

gefundener Kommentar:
// ha: 02/10/31 Erzeuge das Pattern, um Kommentare zu matchen

Fazit

Die Hinzunahme von Regulären Ausdrücken in die Standard Java Technologie ist als sehr sinnvoller Schritt zu bezeichnen und vergrößert das ohnehin schon kaum mehr überschaubare Einsatzgebiet von Java um ein wichtiges Terrain.

In ORDIX Projekten mit Java haben wir diese fundamentale Technik zuvor schon eingesetzt, allerdings mit einem externen Paket (einer GNU Implementierung).

Darüber hinaus beschäftigt sich ORDIX in vielfältiger Weise mit diesem Thema, sei es als Inhalt aktueller Perl- oder "shell, sed, awk"- Seminare oder auch in zahlreichen Projekten mit Perl oder Unix Einsatz.

Dr. Hubert Austermeier (info@ordix.de).