1.はじめに

このクイックチュートリアルでは、文字列内の複数の単語を検出する方法を説明します

2. 私たちの例

文字列があるとしましょう:

String inputString = "hello there, Baeldung";

私たちのタスクは、inputString「hello」および「Baeldung」の単語が含まれているかどうかを確認することです。

それでは、キーワードを配列に入れましょう。

String[] words = {"hello", "Baeldung"};

さらに、単語の順序は重要ではなく、一致は大文字と小文字を区別する必要があります。

3. String.contains()の使用

まず、 String.contains()メソッドを使用して目標を達成する方法を示します。

キーワード配列をループして、 inputString:内の各アイテムの出現を確認しましょう。

public static boolean containsWords(String inputString, String[] items) {
    boolean found = true;
    for (String item : items) {
        if (!inputString.contains(item)) {
            found = false;
            break;
        }
    }
    return found;
}

contains()メソッドは、inputStringに指定されたitemが含まれている場合、trueを返します。 文字列内にキーワードがない場合は、先に進むのをやめて、すぐにfalseを返すことができます。

より多くのコードを作成する必要があるという事実にもかかわらず、このソリューションは単純なユースケースでは高速です。

4. String.indexOf()を使用する

String.contains()メソッドを使用するソリューションと同様に、 String.indexOf()メソッドを使用してキーワードのインデックスを確認できます。 そのためには、inputStringとキーワードのリストを受け入れるメソッドが必要です。

public static boolean containsWordsIndexOf(String inputString, String[] words) {
    boolean found = true;
    for (String word : words) {
        if (inputString.indexOf(word) == -1) {
            found = false;
            break;
        }
    }
    return found;
}

indexOf()メソッドは、inputString内の単語のインデックスを返します。 テキストに単語が含まれていない場合、インデックスは-1になります。

5. 正規表現の使用

それでは、正規表現を使用して単語を一致させましょう。 そのために、Patternクラスを使用します。

まず、文字列式を定義しましょう。 2つのキーワードを一致させる必要があるため、2つの先読みを使用して正規表現ルールを作成します。

Pattern pattern = Pattern.compile("(?=.*hello)(?=.*Baeldung)");

そして一般的な場合:

StringBuilder regexp = new StringBuilder();
for (String word : words) {
    regexp.append("(?=.*").append(word).append(")");
}

その後、 matcher()メソッドを使用して、オカレンスを find()します。

public static boolean containsWordsPatternMatch(String inputString, String[] words) {

    StringBuilder regexp = new StringBuilder();
    for (String word : words) {
        regexp.append("(?=.*").append(word).append(")");
    }

    Pattern pattern = Pattern.compile(regexp.toString());

    return pattern.matcher(inputString).find();
}

しかし、 正規表現にはパフォーマンスコストがかかります。 調べる単語が複数ある場合、このソリューションのパフォーマンスは最適ではない可能性があります。

6. Java8とリストを使用する

そして最後に、Java8のStreamAPIを使用できます。 しかし、最初に、初期データを使用していくつかのマイナーな変換を行いましょう。

List<String> inputString = Arrays.asList(inputString.split(" "));
List<String> words = Arrays.asList(words);

次に、StreamAPIを使用します。

public static boolean containsWordsJava8(String inputString, String[] words) {
    List<String> inputStringList = Arrays.asList(inputString.split(" "));
    List<String> wordsList = Arrays.asList(words);

    return wordsList.stream().allMatch(inputStringList::contains);
}

上記の操作パイプラインは、入力文字列にすべてのキーワードが含まれている場合、trueを返します。

または、コレクションフレームワークのcontainsAll()メソッドを使用するだけで、目的の結果を得ることができます。

public static boolean containsWordsArray(String inputString, String[] words) {
    List<String> inputStringList = Arrays.asList(inputString.split(" "));
    List<String> wordsList = Arrays.asList(words);

    return inputStringList.containsAll(wordsList);
}

ただし、この方法は単語全体に対してのみ機能します。 したがって、テキスト内で空白で区切られている場合にのみ、キーワードが検索されます。

7. Aho-Corasickアルゴリズムの使用

簡単に言えば、 Aho-Corasickアルゴリズムは、複数のキーワードを使用したテキスト検索用です検索するキーワードの数やテキストの長さに関係なく、時間計算量はO(n)です。

Aho-Corasickアルゴリズムの依存関係pom.xmlに含めましょう。

<dependency>
    <groupId>org.ahocorasick</groupId>
    <artifactId>ahocorasick</artifactId>
    <version>0.4.0</version>
</dependency>

まず、キーワードのwords配列を使用してトライパイプラインを構築しましょう。 そのために、Trieデータ構造を使用します。

Trie trie = Trie.builder().onlyWholeWords().addKeywords(words).build();

その後、 inputString テキストを使用してパーサーメソッドを呼び出し、キーワードを検索して結果をemitsコレクションに保存します。

Collection<Emit> emits = trie.parseText(inputString);

そして最後に、結果を印刷すると、次のようになります。

emits.forEach(System.out::println);

キーワードごとに、テキスト内のキーワードの開始位置、終了位置、およびキーワード自体が表示されます。

0:4=hello
13:20=Baeldung

最後に、完全な実装を見てみましょう。

public static boolean containsWordsAhoCorasick(String inputString, String[] words) {
    Trie trie = Trie.builder().onlyWholeWords().addKeywords(words).build();

    Collection<Emit> emits = trie.parseText(inputString);
    emits.forEach(System.out::println);

    boolean found = true;
    for(String word : words) {
        boolean contains = Arrays.toString(emits.toArray()).contains(word);
        if (!contains) {
            found = false;
            break;
        }
    }

    return found;
}

この例では、単語全体のみを検索しています。 したがって、 inputString だけでなく“ helloBaeldung” も照合する場合は、から onlyWholeWords()属性を削除するだけです。 Trieビルダーパイプライン。

さらに、同じキーワードに複数の一致がある可能性があるため、emitsコレクションから重複する要素も削除することに注意してください。

8. 結論

この記事では、文字列内で複数のキーワードを見つける方法を学びました。 さらに、コアJDKとAho-Corasickライブラリを使用した例を示しました。

いつものように、この記事の完全なコードは、GitHubから入手できます。