Перейти к основному содержимому

Проверьте, содержит ли строка несколько ключевых слов в Java

· 5 мин. чтения

1. Введение

В этом кратком руководстве мы узнаем, как обнаружить несколько слов внутри строки .

2. Наш пример

Предположим, у нас есть строка:

String inputString = "hello there, ForEach";

Наша задача — найти, содержит ли inputString слова «hello» и «ForEach» .

Итак, давайте поместим наши ключевые слова в массив:

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

Кроме того, порядок слов не важен, и совпадения должны быть чувствительны к регистру.

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() вернет true , если inputString содержит данный элемент . Когда у нас нет ни одного из ключевых слов внутри нашей строки, мы можем прекратить движение вперед и немедленно вернуть 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 .

Во-первых, давайте определим строковое выражение. Поскольку нам нужно сопоставить два ключевых слова, мы построим наше правило регулярного выражения с двумя прогнозами:

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

И для общего случая:

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

После этого мы будем использовать метод matcher() для поиска вхождений:

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. Использование Java 8 и списка

И, наконец, мы можем использовать Stream API Java 8 . Но сначала давайте сделаем небольшие преобразования с нашими исходными данными:

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

Теперь пришло время использовать Stream API:

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 () фреймворка Collections для достижения желаемого результата:

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. Использование алгоритма Ахо-Корасика

Проще говоря, алгоритм Ахо-Корасика предназначен для поиска текста по нескольким ключевым словам . Он имеет временную сложность O(n) независимо от того, сколько ключевых слов мы ищем или какова длина текста.

Давайте включим зависимость алгоритма Ахо-Корасика в наш pom.xml :

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

Во-первых, давайте создадим конвейер trie с массивом ключевых слов 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=ForEach

Наконец, давайте посмотрим на полную реализацию:

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, но и «helloForEach» , мы должны просто удалить атрибут onlyWholeWords() из конвейера построителя Trie .

Кроме того, имейте в виду, что мы также удаляем повторяющиеся элементы из коллекции emits , поскольку для одного и того же ключевого слова может быть несколько совпадений.

8. Заключение

В этой статье мы узнали, как найти несколько ключевых слов внутри строки. Более того, мы показали примеры с использованием ядра JDK, а также с библиотекой Aho-Corasick .

Как обычно, полный код для этой статьи доступен на GitHub .