Verwenden von indexOf zum Suchen aller Vorkommen eines Wortes in einer Zeichenfolge

1. Übersicht

Die Suche nach einem Zeichenmuster oder einem Wort in einer größeren Textzeichenfolge erfolgt in verschiedenen Feldern. In der Bioinformatik müssen wir beispielsweise möglicherweise ein DNA-Snippet in einem Chromosom finden.

In den Medien finden Redakteure eine bestimmte Phrase in einem umfangreichen Text. Die Datenüberwachung erkennt Betrug oder Spam, indem sie nach verdächtigen Wörtern sucht, die in Daten eingebettet sind.

In jedem Kontext ist die Suche so bekannt und entmutigend, dass sie im Volksmund als „Nadel im Heuhaufen-Problem“ bezeichnet wird . In diesem Tutorial zeigen wir einen einfachen Algorithmus, der die indexOf-Methode (String str, int fromIndex) der Java String- Klasse verwendet, um alle Vorkommen eines Wortes in einem String zu finden.

2. Einfacher Algorithmus

Anstatt einfach das Vorkommen eines Wortes in einem größeren Text zu zählen, findet und identifiziert unser Algorithmus jeden Ort, an dem ein bestimmtes Wort im Text vorhanden ist. Unsere Herangehensweise an das Problem ist kurz und einfach, so dass:

  1. Die Suche findet das Wort auch innerhalb von Wörtern im Text . Wenn wir also nach dem Wort "fähig" suchen, finden wir es in "komfortabel" und "Tablet".
  2. Bei der Suche wird die Groß- und Kleinschreibung nicht berücksichtigt .
  3. Der Algorithmus basiert auf dem naiven String-Suchansatz . Dies bedeutet, dass wir, da wir hinsichtlich der Art der Zeichen im Wort und der Textzeichenfolge naiv sind, Brute Force anwenden, um jede Position des Textes auf eine Instanz des Suchworts zu überprüfen.

2.1. Implementierung

Nachdem wir die Parameter für unsere Suche definiert haben, schreiben wir eine einfache Lösung:

public class WordIndexer { public List findWord(String textString, String word) { List indexes = new ArrayList(); String lowerCaseTextString = textString.toLowerCase(); String lowerCaseWord = word.toLowerCase(); int index = 0; while(index != -1){ index = lowerCaseTextString.indexOf(lowerCaseWord, index); if (index != -1) { indexes.add(index); index++; } } return indexes; } }

2.2. Testen der Lösung

Um unseren Algorithmus zu testen, verwenden wir einen Ausschnitt einer berühmten Passage aus Shakespeares Hamlet und suchen nach dem Wort „oder“, das fünfmal vorkommt:

@Test public void givenWord_whenSearching_thenFindAllIndexedLocations() { String theString; WordIndexer wordIndexer = new WordIndexer(); theString = "To be, or not to be: that is the question: " + "Whether 'tis nobler in the mind to suffer " + "The slings and arrows of outrageous fortune, " + "Or to take arms against a sea of troubles, " + "And by opposing end them? To die: to sleep; " + "No more; and by a sleep to say we end " + "The heart-ache and the thousand natural shocks " + "That flesh is heir to, 'tis a consummation " + "Devoutly to be wish'd. To die, to sleep; " + "To sleep: perchance to dream: ay, there's the rub: " + "For in that sleep of death what dreams may come,"; List expectedResult = Arrays.asList(7, 122, 130, 221, 438); List actualResult = wordIndexer.findWord(theString, "or"); assertEquals(expectedResult, actualResult); }

Wenn wir unseren Test durchführen, erhalten wir das erwartete Ergebnis. Wenn Sie nach "oder" suchen, erhalten Sie fünf Instanzen, die auf verschiedene Weise in die Textzeichenfolge eingebettet sind:

index of 7, in "or" index of 122, in "fortune" index of 130, in "Or index of 221, in "more" index of 438, in "For"

In mathematischen Begriffen hat der Algorithmus eine Big-O-Notation von O (m * (nm)) , wobei m die Länge des Wortes und n die Länge der Textzeichenfolge ist. Dieser Ansatz eignet sich möglicherweise für Heuhaufen-Textzeichenfolgen mit einigen tausend Zeichen, ist jedoch bei Milliarden von Zeichen unerträglich langsam.

3. Verbesserter Algorithmus

Das einfache Beispiel oben zeigt einen naiven Brute-Force-Ansatz für die Suche nach einem bestimmten Wort in einer Textzeichenfolge. Als solches funktioniert es für jedes Suchwort und jeden Text.

Wenn wir im Voraus wissen, dass das Suchwort kein sich wiederholendes Zeichenmuster wie „aaa“ enthält, können wir einen etwas effizienteren Algorithmus schreiben.

In diesem Fall können wir sicher vermeiden, dass die Sicherung durchgeführt wird, um jeden Speicherort in der Textzeichenfolge erneut als potenziellen Startspeicherort zu überprüfen. Nachdem wir die indexOf () -Methode aufgerufen haben , werden wir einfach nach dem Ende des zuletzt gefundenen Vorkommens zum Speicherort verschoben . Diese einfache Optimierung ergibt ein Best-Case-Szenario für O (n) .

Schauen wir uns diese erweiterte Version der früheren findWord () -Methode an.

public List findWordUpgrade(String textString, String word) { List indexes = new ArrayList(); StringBuilder output = new StringBuilder(); String lowerCaseTextString = textString.toLowerCase(); String lowerCaseWord = word.toLowerCase(); int wordLength = 0; int index = 0; while(index != -1){ index = lowerCaseTextString.indexOf(lowerCaseWord, index + wordLength); // Slight improvement if (index != -1) { indexes.add(index); } wordLength = word.length(); } return indexes; }

4. Fazit

In diesem Tutorial haben wir einen Suchalgorithmus vorgestellt, bei dem die Groß- und Kleinschreibung nicht berücksichtigt wird, um alle Variationen eines Wortes in einer größeren Textzeichenfolge zu finden. Lassen Sie sich jedoch nicht davon abhalten , dass die indexOf () -Methode der Java- String- Klasse von Natur aus zwischen Groß- und Kleinschreibung unterscheidet und beispielsweise zwischen „Bob“ und „Bob“ unterscheiden kann.

Insgesamt ist indexOf () eine bequeme Methode, um eine in einer Textzeichenfolge vergrabene Zeichenfolge zu finden, ohne Codierungen für Teilstring-Manipulationen vorzunehmen.

Wie üblich ist die vollständige Codebasis dieses Beispiels auf GitHub beendet.