Wie lassen sich Teilstrings in Strings finden?
Die Klasse java.util.regex.Matcher
stellt
eine Reihe von Methoden bereit, um das Vorkommen von
Teilstrings in einem String zu überprüfen und
deren jeweilige Position zu ermitteln. Durch die Nutzung
eines regulären Ausdrucks kann dabei nach ganzen,
einem Muster entsprechenden Gruppen gesucht werden. Dies
stellt einen Vorteil gegenüber anderen Suchweisen
dar.
Das Klassengerüst
Der Übersichtlichkeit halber werden die einzelnen
Suchverfahren im Folgenden in Methoden gekapselt, die im
Konstruktor einer Beispielklasse aufgerufen werden
können. Der zu durchsuchende Text ist für
einige Beispiel dabei in einer Klassenvariablen TXT
deklariert, sodass sich hierfür das folgende
Klassengerüst ergibt:
import java.util.regex.MatchResult; import java.util.regex.Matcher; import java.util.regex.Pattern; public class StringFinden { private final static String TXT = "Eine Rose ist eine Rose und weder eine famose Hose noch eine lose Dose."; public StringFinden() { // Methodenaufruf } // Methodendeklaration public static void main(String[] args) { new StringFinden(); } }
Suche mittels regulärem Ausdruck
Um die Suche durch einen regulären Ausdruck
vorzubereiten, muss zunächst ein Pattern
-Objekt
in Form des vorkompilierten Ausdrucks erstellt werden.
Dies wird durch die Übergabe seiner
Stringrepräsentation an die statische Methode compile()
der Klasse Pattern
erreicht. Auf diesem
Objekt wird dann ein Matcher
durch Aufruf
von matcher()
erzeugt. Der Methode wird
dabei der zu durchsuchende Textstring als Parameter
übergeben.
Das Matcher
-Objekt
stellt dann die diversen Methoden zur Suche bereit.
Prüfung des vollständigen Strings
private void findAll() { Pattern p = Pattern.compile("\\b.ose.*"); Matcher m = null; for (int i = 0; i < TXT.length(); i++) { m = p.matcher(TXT.substring(i)); if (m.matches()) { System.out.println(m.group()); } } }
Die Methode Matcher#matches()
überprüft den vollständigen
String auf Übereinstimmung mit dem regulären
Ausdruck. Sie kann hierdurch z.B. Verwendung finden, um
innerhalb einer Schleife mehrere Textbereiche
nacheinander zu überprüfen.
Das Beispiel
oben durchläuft den Textstring zeichenweise und
vergleicht das jeweilige Fragment von der Indexposition
bis zum Ende mit dem regulären Ausdruck. Die
Ausgabe ist wie folgt:
Rose ist eine Rose und weder eine famose Hose noch eine lose Dose. Rose und weder eine famose Hose noch eine lose Dose. mose Hose noch eine lose Dose. Hose noch eine lose Dose. lose Dose. Dose.
Die Tatsache, dass wirklich der vollständige String
dem Ausdruck entsprechen muss, lässt sich leicht
überprüfen, indem man den regulären
Ausdruck von \\b.ose.*
zu \\b.ose
abändert. Es findet dann keine Ausgabe statt, da
nach der Silbe 'ose' in jedem Fall mindestens
noch ein Zeichen folgt.
Zur Ausgabe des gefundenen
Musters, wird hier Matcher#group()
herangezogen.
Prüfung des String-Anfangs
private void findAllFromBeginning() { Pattern p = Pattern.compile("\\b.ose"); Matcher m = p.matcher(TXT); for (int i = 0; i < TXT.length(); i++) { m = p.matcher(TXT.substring(i)); if (m.lookingAt()) { System.out.println(m.group()); } } }
Die Methode Matcher#lookingAt()
findet wie
Matcher.matches()
ebenfalls Einträge,
die am Beginn des Textstrings stehen,
berücksichtigt jedoch ggfs. nicht das Ende des
Textstrings. Obige Methode findAllFromBeginning()
erzeugt die folgende Ausgabe:
Rose Rose mose Hose lose Dose
Erstes Vorkommen finden
private void findPart() { Pattern p = Pattern.compile("(\\b.ose)"); Matcher m = p.matcher(TXT); if (m.find()) { System.out.println(m.group() + " found at index " + m.start() + " ending at " + m.end()); } }
Die Methode Matcher#find()
findet das erste
Vorkommen des Patterns innerhalb eines Strings und
liefert erfolgsabhängig einen boolschen Wert. Die
Anfangsposition des ermittelten Teilstrings kann durch Matcher#start()
ermittelt werden. Die erste Position nach dem erkannten
String lässt sich durch Matcher#end()
finden. Die Methode liefert die folgende Ausgabe:
Rose found at index 5 ending at 9
Alle Vorkommen finden
private void findMultiPart() { Pattern p = Pattern.compile("(\\b.ose)"); Matcher m = p.matcher(TXT); while (m.find()) { System.out.println(m.group() + " found at index " + m.start()); } }
Um alle Vorkommen eines Patterns in einem Textstring zu
finden, kann Matcher#find()
als
Abbruchbedingung in einer While-Schleife genutzt werden.
Der String wird durchlaufen und die Schleife terminiert,
wenn kein Pattern mehr gefunden wird und find()
somit false
zurückgibt.
Die Ausgabe
von findMultiPart()
:
Rose found at index 5 Rose found at index 19 Hose found at index 46 lose found at index 61 Dose found at index 66
Suche im Stream
private void findStream() { Pattern p = Pattern.compile("(\\b.ose)"); Matcher m = p.matcher(TXT); m.results().map(MatchResult::group).forEach(s -> { System.out.println(s + " found at index " + m.start()); }); }
Ab Java 9 kann die Patternsuche auch per Stream
erfolgen. Hierzu liefert Matcher#results()
einen Stream<MatchResult>
in der
Reihenfolge des Pattern-Vorkommens. MatchResult#group()
gibt dann die jeweils gefundene Sequenz zurück. Die
Ausgabe der obigen Methode findStream()
entspricht derjenigen von findMultiPart()
.
Suchen - Ersetzen
Die Klasse Matcher
stellt eine Reihe von
Methoden bereit, die es ermöglichen, eine
Suchen-Ersetzen-Funktion zu implementieren. Sollen alle
dem Pattern entsprechenden Stringfragmente ersetzt
werden, kann hierzu Matcher#replaceAll()
verwendet werden. Der Methode wird hierzu der
Ersatzstring als Parameter übergeben:
private void replaceAll() { Pattern p = Pattern.compile("(\\b.ose)"); Matcher m = p.matcher("Eine Rose ist eine Rose und kein Krokus"); String replaced = m.replaceAll("Tulpe"); System.out.println(replaced); }
Die Ausgabe zeigt:
Eine Tulpe ist eine Tulpe und kein Krokus
Soll nur das erste Vorkommen im String ersetzt werden,
kann statt replaceAll()
replaceFirst()
Verwendung finden.
Eine weitere Möglichkeit ergibt sich, wenn das
Ergebnis in einem StringBuffer
-Objekt
gespeichert werden soll, etwa bei der Bearbeitung sehr
langer Texte. Hierzu wird Matcher#appendReplacement()
genutzt. Der Methode werden zwei Parameter
übergeben. Der erste, ein StringBuffer
speichert das Ergebnis abschnittsweise nach erfolgtem
Ersetzen, der zweite ist der String, der zum Austausch
dient.
private void findReplace() { Pattern p = Pattern.compile("(\\b.ose)"); Matcher m = p.matcher("Eine Rose ist eine Rose und kein Krokus"); StringBuffer sb = new StringBuffer(); while (m.find()) { m.appendReplacement(sb, "Tulpe"); } System.out.println(sb.toString()); m.appendTail(sb); System.out.println(sb.toString()); }
Das Verfahren läuft hierbei folgendermaßen ab:
Durch Matcher#find()
wird über den
Textstring iteriert. Wird das Pattern erkannt - und nur
dann - werden der Stringbereich von Beginn an bis zum
letzen Character vor der erkannten
Character-Sequenz mit danach angefügtem
Ersatzstring in den StringBuffer
kopiert.
Wird in der Folge ein weiteres Vorkommen gefunden, so
wird auf die gleiche Weise nach dem Ende des
vorhergehenden Fundes fortgefahren. Ein
abschließender Teil des Textes, der nicht dem
gesuchten Pattern entspricht, wird auf diese Weise
jedoch nicht in den StringBuffer
kopiert.
Dies geschieht nach Abschluss der Suche durch Matcher#appendTail()
.
Die Ausgabe zeigt:
Eine Tulpe ist eine Tulpe Eine Tulpe ist eine Tulpe und kein Krokus
Wenn Ihnen javabeginners.de gefällt, freue ich mich über eine Spende an diese gemeinnützigen Organisationen.