Inselupdate: Suchen und Ersetzen mit Mustern

Für den Suchfall mit Java RegEx gibt es noch eine Erweiterung, dass nämlich die Pattern-Klasse die Fundstellen nicht nur ermittelt, sondern sie auch durch etwas anderes ersetzen kann.

Beispiel: In einem String sollen alle Nicht-JVM-Sprachen ausgepiept werden:

String  text    = „Ich mag Java, Groovy und ObjectiveC und PHP.“;
Matcher matcher = Pattern.compile(„ObjectiveC|PHP“ ).matcher( text );
StringBuffer sb = new StringBuffer();
while ( matcher.find() )
matcher.appendReplacement( sb, „[PIEP]“ );
matcher.appendTail( sb );
System.out.println( sb );  // Ich mag Java, Groovy und [PIEP] und [PIEP].

Um mit dem Mechanismus „Suchen und Ersetzen“ zu arbeiten, wird zunächst ein Container vom Typ StringBuffer aufgebaut, denn in dem echten String kann Pattern die Fundstellen nicht ersetzen. (Leider ist ein StringBuffer nötig, die API akzeptiert keinen StringBuilder.) Erkennt der Matcher ein Muster, macht appendReplacement(…) zwei Dinge:

1. Die Methode füllt den Container mit allen Zeichen vom letzten Fund bis zur jetzigen Fundstelle auf. Beim ersten Aufruf ist das „Ich mag Java, Groovy und “, dann folgt „ und “.

2. In den StringBuffer können wir unsere Ersetzung, in diesem Falle „[PIEP]“, setzen.

So wächst der StringBuffer von Schritt zu Schritt. Nach der letzten Fundstelle setzt appendTail(…) das noch verbleibende Teilstück von der letzten Funstelle bis zum Stringende in den StringBuffer-Container.

Im Prinzip können wir in der while-Schleife mit matcher.group(…) auf das Fundstück zurückgreifen und es in die Ersetzung einbauen. Doch toll an appendReplacement(…) ist, dass der Ersetzungsstring ein $ enthalten darf – mit dem Problem, dass ein vorkommendes Dollar-Zeichen ausmaskiert werden muss –, der Zugriff auf die Suchgruppe bietet. Damit lassen sich sehr elegante Lösungen bauen. Nehmen wir an, wir müssen in einer Zeichenkette alle URLs in HTML-Hyperlinks konvertieren. Dann rahmen wir einfach jede Fundstelle in die nötigen HTML-Tags ein. In Quellcode sieht das so aus:

Listing 2.7: RegExSearchAndReplace.java, main()

String  text    = „Hi, schau mal bei http://stackoverflow.com/ “ +
                  „oder http://www.tutego.de/ vorbei.“;
String  regex   = „http://[a-zA-Z0-9\\\]+\\.[a-zA-Z]{2,3}(\\S*)?“;
Matcher matcher = Pattern.compile( regex ).matcher( text );
StringBuffer sb = new StringBuffer( text.length() );
while ( matcher.find() )
  matcher.appendReplacement( sb, „<a href=\“$0\“>$0</a>“ );
matcher.appendTail( sb );
System.out.println( sb );

Der StringBuffer enthält dann zum Schluss „Hi, schau mal bei <a href=“http://stackoverflow.com/“>http://stackoverflow.com/</a> oder <a href=“http://www.tutego.de/“>http://www.tutego.de/</a> vorbei.“. (Der gewählte reguläre Ausdruck für URLs ist kurz, aber nicht vollständig. Für das Beispiel spielt das aber keine Rolle.)

Tipp: Die String-Methoden replaceAll(…) und replaceFirst(…) ersetzen direkt, und arbeiten im Hintergrund genauso. Zum Einsatz kommt die replaceAll(.,.)-Methode vom Matcher.

Hinweis: Der Ersetzungsausdruck „<a href=\“$0\“>$0</a>“ enthält mit $ Steuerzeichen für den Matcher. Wenn die Ersetzung aber überhaupt nicht mit $n auf das gefundene Wort zurückgreift, sollten die beiden Sonderzeichen \ und $ ausmaskiert werden. Auf diese Weise werden merkwürdige Fehler vermieden, wenn doch in der Ersetzung ein Dollar-Zeichen oder ein Backslash vorkommt. Das Ausmaskieren übernimmt die Methode quoteReplacement(…), sodass sich zum Beispiel Folgendes ergibt:

matcher.appendReplacement( sb, Matcher.quoteReplacement( replacement ) );

Über Christian Ullenboom

Ich bin Christian Ullenboom und Autor der Bücher ›Java ist auch eine Insel. Einführung, Ausbildung, Praxis‹ und ›Java SE 8 Standard-Bibliothek. Das Handbuch für Java-Entwickler‹. Seit 1997 berate ich Unternehmen im Einsatz von Java. Sun ernannte mich 2005 zum ›Java-Champion‹.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.