Many script programming languages have regular expression and most of modern compiler based programming languages also. This document is an introduction for using regular expressions.

The three parts of introduction are the base concepts of regular expressions, base concepts for Java programming languages and the Java practice.

The regular expression in Java are nearly identical to Perl implementation. If you can write Perl regex, also you can it in Java, but read the documentation for class Pattern in package java.util.regex before.

regular expressions

Regular expressions are syntax defined pattern for using at datas. Give your some times to understand the syntax, it is not easy.

Stones of regular expressions

So that you can formulate regular expressions, you must know the single stones. As soon as you is a matter controlling this only even to assemble this properly. At last you are the architect of a building site which it is a matter to construct. Besides, regular expressions exist of three parts:

  1. The atomic stones (atoms)
  2. Die components (pieces)
  3. Die areas (Ranges)

 

The atomic stones (atoms)

The atomic stones are the smallest indivisible regular expressions. Every regular expression can be looked as a combination of these stones.

atomic stones of regular expressions
definition example / syntax description
a single sign s corresponds  sign s
any sign . the dot corresponds any sign
line beginning ^ the caret corresponds the line beginning
line ending $ the dollar sign corresponds the end of line sign / line delimiter: "\n", "\r\n", "\r", "\u0085", "\u2028" and "\u2029" accepted
masksign \ with backslash if they can also take up sonderzeichen in her muster
grouped included pattern (ei) by grouped muster theirs let themselves muster further speziallisieren (see also grouping).
group delimiter | by group delimiter or also OR-operator can be specified grupierung further.
atomic stones of regular expressions
Note that a complete pattern can be also seen as an atom if you declare it as a grouping.

Die Bauteile (Pieces)

Die Bauteile (pieces) sind die atomaren Bausteine der regulären Ausdrücke, erweitert um Quantifizierer. Diese Quantifizierer geben an, wie oft ein bestimmter atomarer Ausdruck vorkommen darf, soll oder muss.

Bauteile reguläre Ausdrücke
Definition Beispiel / Syntax Erläuterung
ein weiterer Baustein bastie Entspricht sechs aneinandergereihten Bausteinen. Die automaren Bausteine werden aneinander angefügt.
null (0) oder beliebig viele der Bausteine * Dem vorangegangenen Baustein wird ein Asterix (*) angefügt.
ein (1) oder beliebig viele der Bausteine + Dem vorangegangenen Baustein wird ein Plus (+) angefügt.
null (0) oder ein (1) Baustein ? Dem vorangegangenen Baustein wird ein Asterix (?) angefügt.
eine genaue Anzahl der Bausteine [0-9]{2} Entspricht dem regulären Ausdruck "[0-9][0-9]". In geschweifen Klammern wird dem Baustein die gewünschte Anzahl angefügt.
ein Anzahl in einem bestimmte Wertebereich [0-9]{1,3} Der Baustein muss hier mindestens ein mal, darf jedoch höchstens jedoch drei mal vorkommen. In geschweiften Klammern wird dem Baustein das gewünschte Minimum un Maximum getrennt durch ein Kommata angefügt.
Übersicht der Bauteile regulärer Ausdrücke

Die Bereiche (Ranges)

Die "Faulheit" der Programmierer hat sich letztlich in den Bereichen niedergeschlagen. Diese helfen Ihnen eine Menge schreibarbeit zu vermeiden.

Bereiche reguläre Ausdrücke
Definition Beispiel / Syntax Erläuterung
eine Menge von Zeichen [äöüßÄÖÜ] Jedes dieser Zeichen in dem Bereich erfüllt den regulären Ausdruck.
einen Bereich von Zeichen [a-f] Die Zeichen a b c d e f erfüllen den regulären Ausdruck.
nicht dieses Zeichen [^,] Das dem Caret nachgestellte Zeichen erfüllt nicht den regulären Ausdruck.
nicht die Zeichen des Bereichs [^0-9] Der Bereich der Zeichen nach dem Caret erfüllt nicht den regulären Ausdruck.
Übersicht der Bereiche regulärer Ausdrücke

Die Gruppierung

Mit Hilfe der Gruppierung haben Sie die Möglichkeit häufig benötigte Aufgabenstellungen umzusetzen. Eine Gruppierung wird dabei durch rund Klammern spezifiziert z.B. "(ä|ü|ö)". Zusammen mit dem Delimiter / OR-Operator ermöglicht dies spezielle Muster z.B. zur Suche nach ähnlichen Ausdrücken. Der reguläre Ausdruck "M(ey|ei|ay|ai)er" stellt somit vier verschiedene Schreibweise von Herrn oder Frau Meier dar. Gleiches ließe sich auch mit dem kürzeren regulären Ausdruck "M(e|a)(y|i)er" realisieren.

Besondere Notationen

Die schon erwähnte Faulheit der Programmierer hat zu weiteren Abkürzungen geführt, die noch kurz dargestellt werden sollen.

Bereiche reguläre Ausdrücke
Definition Abkürzung normale Definition
eine Ziffer \d [0-9]
keine Ziffer \D [^0-9]
ein Buchstabe oder eine Zahl (ohne landesspezifische Zeichen) inkl. Kleinbuchstaben \w [a-zA-Z0-9]
kein Buchstabe und keine Zahl (ohne landesspezifische Zeichen) inkl. Kleinbuchstaben \W [^a-zA-Z0-9]
ein nicht druckbares Zeichen (Leerraum) \s [\t\n\r\f]
kein nicht druckbares Zeichen (Leerraum) \S [^\t\n\r\f]
Übersicht der Bereiche regulärer Ausdrücke

Reguläre Ausdrücke in Java

Mit dem J2SDK 1.4.0 hat Sun Microsystems den Schritt Richtung reguläre Ausdrücke gewagt. Vor dieser Version war die Verwendung von regulären Ausdrücken nur mit Hilfe von Drittanbieter APIs oder eigener Implementation z.B. mit Hilfe der Klasse StringTokenizer möglich. Das kleine zugehörige Framework findet sich im Paket java.util.regex und besteht lediglich aus den Typen Pattern, Matcher und PatternSyntaxException. Zu diesen kommt jedoch noch das Interface CharSequence aus dem java.lang Paket.
Die Klassen Pattern und Matcher sind dabei als final deklariert. Die Klasse Pattern beinhaltet den regulären Ausdruck, während die Klasse Matcher sich um die Auswertung des selben kümmert.

Anm. Überlegen Sie stets, ob Sie wirklich einen Regulären Ausdruck verwenden wollen / müssen. Häufig kann ein einfacher StringTokenizer oder ein substring gleiches bewirken und leichter zu lesen sein.

Die Klasse Pattern

Da die Klasse selbst keinen öffentlichen Konstruktor hat, müssen wir uns der Fabrikmethode compile bedienen. Dieser übergeben Sie Ihr konstruiertes Muster, ggf. gefolgt von speziellen Optionen (flags).
Die Optionen selbst sind dabei als statische Konstanten der Klasse Pattern erreichbar.

Optionen
Definition Erläuterung
CANON_EQ
UNICODE_CASE Mit Hilfe dieses Flags können Sie auch in Unicode-Zeichenketten die Groß- und Kleinschreibung berücksichtigen. Die Performance Ihrer Javaanwendung kann darunter jedoch erheblich leiden.
DOTALL In diesem Modus bekommt der Punkt (.) einen erweiterten Wertebereich, so dass auch Zeilentrennzeichen berücksichtigt werden.
MULTILINE Diese Option ermöglicht die Suche unter Berücksichtigung von Zeilentrenner ("\n", "\r\n", "\r", "\u0085", "\u2028" und "\u2029"). Sofern Sie das Caret (^) oder Dollar ($) -Muster nutzen wollen ist diese Option meist einzuschalten.
COMMENTS Diese Option erlaubt innerhalb des regulären Ausdrucken Kommentare einzufügen. Diese beginnen mit dem Wert-Zeichen (#) und enden mit einem Zeilentrenner.
CASE_INSENSITIVE Bei der Prüfung des Musters / regulären Ausdrucks wird keine Rücksicht auf die Groß- / Kleinschreibung genommen.
UNIX_LINES Hierbei wird als Zeilentrenner lediglich "\n" berücksichtigt.
Optionen für die Verarbeitung regulärer Ausdrücke
Mehrere Optionen können Sie durch summieren der einzelnen Konstanten einaschalten.
Alternativ dazu können wir auf ein Objekt vom Typ CharSequence mit Hilfe der Klassenmethode matches direkt unser Muster prüfen. Sie können so prüfen, ob Ihr Muster / regulärer Ausdruck innerhalb des CharSequence-Objektes gefunden wurde.

Die Klasse Matcher

Auch wenn Sie mit der Klasse Pattern Ihr Muster definieren, so ist der wahre Künstler die Klasse Matcher. Mit Hilfe dieser Klasse können Sie Muster finden, zählen, ersetzen...
Ein Objekt dieses Typs erzeugen Sie, in dem Sie die Fabirkethode matches eines Pattern-Objektes aufrufen. Als Parameter wird die Zeichenkette erwartet, auf welcher Sie Ihr Muster anwendenden wollen. Anschliessend stehen Ihnen verschiedenen Methoden zur Verfügung.

Ihr Matcher-Objekt stellt Ihnen nunmehr zahlreiche Operationen zum Testen, Aufbereitung und Ändern bereit. Zu diesen gehören auch:

  • matches()
    für die Prüfung ob Ihre Zeichenkette exakt Ihrem definierten Muster entspricht.
  • lookingAt()
    für die Prüfung, ob Ihre Zeichenkette mit Ihrem definierten Muster beginnt.
  • find()
    für die Prüfung, ob nach der aktuellen Suchposition eine weitere Musterübereinstimmung in Ihrer Zeichenkette vorkommt. In diesem Zusammenhang gibt Ihnen die Operation start() die aktuelle Suchposition zurück.
  • group()
    für die Aufbereitung / Ausgabe Ihrer gefundenen Übereinstimmgung.
  • group(int gruppe)
    für die Aufbereitung / Ausgabe Ihrer gefundenen Übereinstimmung für ein spezielles gruppiertes eingebettetes Ihrer Muster. Durch die Übergabe von 0 erreichen Sie die Funktionalität der Operation group()
  • Matcher appendReplacement (StringBuffer, String)
    Diese Methode fügt alle Zeichen bis zur nächsten Übereinstimmung dem StringBuffer hinzu. Zusätzlich wird das Muster noch durch den übergebenen String ersetzt und ebenfalls zum StringBuffer hinzugefügt.
  • StringBuffer appendTail (StringBuffer)
    Diese Methode fügt den Rest der Zeichenkette, ab der letzten Position einer Übereinstimmung, dem StringBuffer hinzu.
  • String replaceAll (String)
    Ersetzt jedes gefundene Muster durch die übergebene Zeichenkette.
  • String replaceFirst (String)
    Diese Methode ersetzt die erste Übereinstimmung des Musters durch die übergebene Zeichenkette und liefert das Ergebnis als String zurück.
Es ist anzumerken, dass die Zeichenkette, welche als Parameter an die Methode matcher übergeben wird, durch die oberen Operationen nicht verändert wird. Daher wird entweder ein neuer String zurückgegeben oder als Parameter ein Objekt vom Typ StringBuÿer erwartet. Falls man das bestehende Suchpattern erhalten will, doch die zu parsende Zeichenkette ändern möchte, kann dies mit Hilfe der Methode reset(CharSequence input) erreicht werden. reset ohne Parameter aufgerufen, setzt den Matcher auf den Initialzustand zurück.

 

Das Interface CharSequence

Das Interface CharSequence legt fest, welche Operationen entsprechende Typen bieten müssen. Es bietet einen lesenden Zugriff auf wesentliche Eigenschaften einer Zeichenkette. Neben Ihren Implementationen sind Objekte der Klassen CharBuffer, String und StringBuffer ebenfalls vom Typ CharSequence. CharSequence Objekte stellen folgende Operationen zur Verfügung:

  • char charAt (int index)
    Gibt das Zeichen am Offset "index" zurück.
  • int length ()
    Gibt die Länge der Zeichenkette zurück.
  • CharSequence subSequence (int start, int ende)
    Erstellt eine neue Zeichenkette, die eine Kopie der aktuellen Zeichenkette von "start"-Index (inklusive) bis zum "ende"-Index (exklusive) darstellt.
  • String toString ()
    Erstellt ein String-Objekt, welches eine genaue Kopie der Zeichenkette darstellt.

 

Die PatternSyntaxException

Die PatternSyntaxException wird dann geworfen, wenn Sie versuchen ein Muster zu kompilieren ("Pattern.compile(String muster)") welches nicht der Syntax für Muster entspricht. Sie können diese jedoch sinnvoll nutzen, um z.B. nur zulässige Muster als Benutzereingabe zuzulassen oder auch nur um Ihre eigenen Muster zu prüfen.

Reguläre Ausdrücke mit Java anwenden

Nach der ganzen grauen Theorie - nun gut soviel war es ja doch nicht - nun aber zur Praxis! Zu diesem Zweck werden Sie hier einige Beispiele sehen, die (hoffentlich zumindes teilweise auch bei Ihnen) praktisch und nutzbar sind.

 

Splitter für CSV-Dateien

Ausgangspunkt für dieses Beispiel soll eine alte Batch-Implementation sein, die diesen Zweck für zweispaltige Datencontainer erfüllte:

  for /F "eol=# tokens=1-2 delims=;,|" %%i in (input.csv) do (
    set %%i=%%j
  )
Batchprogrammierer werden gleich Zweck und Erweiterung des Formats erkennen. Dies soll uns jedoch nicht interessieren.
Wichtig für unsere Betrachtung sind die Trennzeichen (delimiters), welche hier das Semikolion ";", das Kommata "," und das "|" sind.
Pattern csvPattern = Pattern.compile("(,|\\||;)");
String [] csvPieces = csvPattern.split (inputString);
for (int i = 0; i < csvPieces.length; i++) {
  System.out.println (csvPieces[i]);
}
Eigentlich ganz einfach, oder? Lediglich das Muster wollen wir nochmal genauer betrachten.
Zuerst nehmen wir eine Gruppierung vor, daher beginnt und endet unser Muster mit einer runden Klammer "()". Anschließend geben wir unser Muster separiert vom Gruppierungstrenner "|" ein. Das mittlere Muster muß zudem noch doppelt maskiert werden: Da wir den Trenner als Muster benötigen müssen wir diesen mit Hilfe des Backslash "\" maskieren. Damit wir jedoch einen Backslash in einem String-Objekt erhalten müssen wir diesen wiederum mit einem Backslash maskieren. Das Muster "(;|,|\\|)" ist mit dem obigen funktionsgleich aber vielleicht etwas übersichtlicher.
Natürlich läßt sich selbiger Effekt auch mit Hilfe der Klasse StringTokenizer erledigen. Eine Implementation würde dementsprechend etwa folgendes aussehen haben.
StringTokenizer tokenizer = new StringTokenizer (input, ";,|", false);
while (tokenizer.hasMoreTokens ()) {
  System.out.println (tokenizer.nextToken ());
}
Wo liegt nun der Vorteil? Eine kleine Übung wird Ihnen hier helfen.
  1. Erstellen Sie eine Javaanwendung ohne Verwendung von Mustern, um einen nach HTML-Syntax formulierten String anhand der HTML-Tags zu trennen!
  2. Überlegen Sie sich ein passendes Muster zu diesem Zweck und testen Sie dieses!
Ein mögliches Muster finden Sie im Abschnitt Lösungen.

 

Textsuche

Eine der wesentlichesten Aufgaben ist das Finden von Zeichenketten, welche mit dem Muster übereinstimmen. Dabei wird nicht wie bei String.indexOf(String) eine genaue Übereinstimmung sondern ein Übereinstimmung mit einem Muster gesucht. Warum? Nicht immer können Sie im voraus alle Einzelheiten spezifizieren. Angesprochen wurde bereits der Hr. Meyer oder war es Maier? Das nachfolgende Beispiel zeigt das Auffinden aller HTML-Tags, die eine Referenz auf eine weitere Resource in Form eines A-Tags darstellen.

    final String input = "<h1>Linkliste</h1>"
                       + "<a href="http://www.ritter.biz/java/howto/index.html">Make Java - How To</a>"
                       + "<a href="http://www.ritter.biz/java/mjregular/index.html">Make Java - Regular expression</a>"
                       + "<a href="http://www.ritter.biz/java/mjperformance/chapter0.html">Make Java - Performance</a>"
                       + "";
    Pattern p = Pattern.compile ("<a>]*>", Pattern.CASE_INSENSITIVE);
    Matcher m = p.matcher (input);
    while (m.find()) {
      System.out.println (m.group());
    }
Mit Hilfe der Methode "group()" lassen wir uns schließlich die Übereinstimmungen zurückgeben.

 

HTML zu Text konvertieren

Die Klasse Matcher stellt zwei Methoden zur Verfügung um Ersetzungen vorzunehmen. Diese (replaceFirst und replaceAll) werden generell gleich verwendet. Wenn Sie aus einer HTML-Datei eine Text-Datei erstellen wollen müssen Sie u.a. die maskierten Sonderzeichen wie z.B. die deutschen Umlaute umsetzen. Dazu dient uns die Methode replaceAll. Dazu zuerst der Java-Quelltext:

    Properties replacingProperties = new Properties ();
    try {
      replacingProperties.load (
              new BufferedInputStream (
                      this.getClass().getResourceAsStream("html2txt.rpl")));
    }
    catch (final IOException exitException) {
      System.err.println ("Fehler beim Laden der Ersetzungstabelle.");
      System.exit (-1);
    }
    String inputString = "<body><h1>Make Java<span lang="lang">"
                       + "regular expression</span> <p>Eine "
                       + "Einführung in Java und reguläre Ausdrücke</p>";
    Enumeration replacingStrings = replacingProperties.keys();

    while (replacingStrings.hasMoreElements()) {
      final String codingString = replacingStrings.nextElement().toString();
      final Pattern p = Pattern.compile (codingString);
      Matcher m = p.matcher(inputString);
      if (m.find()) {
        inputString = new StringBuffer (m.replaceAll (replacingProperties.getProperty(codingString)));
      }
    }
    Pattern htmlTagPattern = Pattern.compile("]*>");
    String [] preText = htmlTagPattern.split (inputString);
    for (int i = 0; i < preText.length; i++) {
      System.out.print (preText[i]);
    }
In dem Beispiel wurde unsere Ersetzungtabelle bereits ausgelagert. Diese sieht ausschnittsweise so aus:
#Ersetzungstabelle von html nach txt
#Sat Aug 31 19:12:31 CEST 2002
™=(tm)
&=\u00DF
®=(r)
Ä=\u00C4
¶=
ü=\u00FC
©=(c)
»=>>
Ü=\u00DC
ö=\u00F6
<br /<=\n\r
<=<
"="
«=<<
Ö=\u00D6
ß=\u00DF
ä=\u00E4
<p>=\n\r\n\r
Die wesentliche Zeile ist "m.replaceAll (replacingProperties.getProperty(codingString))", welches sich z.B. auf "m.replaceAll ("\u00DC")" reduziert, wenn das Muster "Ü" entspricht. Einfach oder?

 

Vor- und Nachmustersuche

Die Vor- und Nachmustersuche ist leider nicht direkt implementiert. Mit Hilfe der Operationen "start()" und "end()" können Sie jedoch die Position eines gefunden Musters extrahieren. Daneben stellt die Methode "split()" diese Funktionalität bereit (siehe Splitter für CSV-Dateien);
Hier folgen jetzt noch zwei Übungsaufgaben, wobei eine mögliche Umsetzung im Abschnitt Lösungen zu finden ist.

  1. Extrahieren Sie aus einem HTML-String alle externen Links (ohne Anker) mit Hilfe der "split()" Methode!
  2. Überlegen Sie sich, wie eine Umsetzung mit Hilfe der "start()" und "end()" Methoden zu implementieren wäre!

 

Musterbeispiele

 

MusterAufgabe
Musterbeispiele
(;|,|\\|) Findet alle CSV Delimiter (siehe Splitter für CSV-Dateien).
]*> Findet alle HTML-Tags in einem HTML Dokument (sieheTextsuche).
<a>]*href=\"?[^(>| )]*\"?[^>]*>[^<]* Findet alle Links in einem HTML-Dokument, jedoch ohne die Anker zu berücksichtigen. Der umschließende HTML-Tag wird dabei mitgeliefert.

Lösungen

 

  1. Muster-String-Objekt: "]*>"
    Eine Beispielanwendung könnte somit in etwa so aussehen:
    package biz.ritter.regex.sample;
    import java.util.regex.*;
    
    /**Überschrift: Pattern und Matcher
     * Copyright: Copyright © 2009
     * Organisation: http://www.ritter.biz
     * @author Copyright © 2009 Sebastian Ritter
     * @licence BSD
     * @version 1.0
     */
    
    public class SimpleHtml2Txt implements Runnable{
    
      /** Defaultconstruktor */
      public SimpleHtml2Txt() {
      }
    
      /**
       * Threadoperation
       */
      public void run () {
        String inputString = ">body>:<h1>Make Java<span lang="lang">"
                           + "regular expression</span> <p>Eine "
                           + "Einführung in Java und reguläre Ausdrücke</p>";
        Pattern htmlTagPattern = Pattern.compile("]*>");
        String [] preText = htmlTagPattern.split (inputString);
        for (int i = 0; i < preText.length; i++) {
          System.out.print (preText[i]);
        }
      }
    
      /**
       * Java-startoperation
       * @param args
       */
      public static void main(String[] args) {
        SimpleHtml2Txt html2Txt = new SimpleHtml2Txt();
        html2Txt.run();
      }
    }
  2. Eine Umsetzung mit Hilfe der "split()" Methode die externen Links einer HTML-Datei zu extrahieren könnte so aussehen:
        input = "<h1>Meine kleine Linkliste</h1><a href="http://www.Ritter.biz/java/mjperformance/chapter0.html">"
              + "Make Java - Performance</a><br />"
              + "<a href="/href">Make Java - How To"
              + "<a target="target" href="/href">Make Java - How To</a>"
              + "<a href="/href">Make Java - Regular expression</a>"
              + "<a href="/href">oben</a>";
        Pattern linkPattern = Pattern.compile ("<a>]*href=\"?[^(>| )]*\"?[^>]*>[^)");
        Matcher linkMatcher = linkPattern.matcher (input);
        LinkedList linkLinkedList = new LinkedList ();
        while (linkMatcher.find ()) {
          linkLinkedList.addLast (linkMatcher.group ());
        }
        for (int i = 0; i < linkLinkedList.size (); i++) {
          CharSequence cs = prePattern.split ((CharSequence)linkLinkedList.get(i))[1];
          cs = postPattern.split (cs)[0];
          System.out.println (cs);
        }
Benötigen Sie noch weitere Anregungen für Übungen? Wie wäre es mit folgenden Anwendungen:
  • Erweitern Sie den HTML zu Text Konvertern, so dass dieser praktisch anwendbar ist.
  • Erstellen Sie eine Anwendung, die einen Java Quelltext in ein HTML Dokument umwandelt. Dabei sollen, Schlüsselwörter, Operatoren, Variablen etc. durch entsprechende HTML-CSS-Klassen näher bestimmt werden können. Alternativ könnten Sie auch einen Syntaxchecker oder Konverter für eine beliebige andere Sprache bauen.
  • Wer etwas mehr Zeit hat, könnte auch einen JDBC-Treiber für CSV Datenbanken schreiben.
  • Schreiben Sie eine kleine Adressenverwaltung, die nur gültige eMail Adressen zuläßt.

Performante reguläre Ausdrücke

Informationen zu performanten regulären Ausdrücken finden Sie im Performance Handbuch unter