1. введение
В этом кратком руководстве мы узнаем, как обнаружить несколько слов внутри строки .
2. Наш Пример
Предположим, у нас есть строка:
String inputString = "hello there, Baeldung";
Наша задача состоит в том, чтобы найти, содержит ли входная строка | слова/| “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() вернет true , если входная строка содержит данный элемент . Когда у нас нет ни одного из ключевых слов внутри нашей строки, мы можем прекратить движение вперед и немедленно вернуть false .
Несмотря на то, что нам нужно написать больше кода, это решение является быстрым для простых случаев использования.
4. Использование String.indexOf()
Аналогично решению, использующему метод String.contains () , мы можем проверить индексы ключевых слов с помощью метода String.indexOf () /. Для этого нам нужен метод, принимающий входную строку и список ключевых слов:
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() возвращает индекс слова внутри входной строки /. Когда у нас нет слова в тексте, индекс будет равен -1.
5. Использование Регулярных Выражений
Теперь давайте использовать регулярное выражение , чтобы соответствовать нашим словам. Для этого мы будем использовать класс Pattern .
Во-первых, давайте определим строковое выражение. Поскольку нам нужно сопоставить два ключевых слова, мы построим наше правило регулярных выражений с двумя точками обзора:
Pattern pattern = Pattern.compile("(?=.*hello)(?=.*Baeldung)");
И для общего случая:
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 и списка
И, наконец, мы можем использовать потоковый API Java 8 . Но сначала давайте сделаем некоторые незначительные преобразования с нашими исходными данными:
ListinputString = Arrays.asList(inputString.split(" ")); List words = Arrays.asList(words);
Теперь пришло время использовать Stream API:
public static boolean containsWordsJava8(String inputString, String[] words) { ListinputStringList = Arrays.asList(inputString.split(" ")); List wordsList = Arrays.asList(words); return wordsList.stream().allMatch(inputStringList::contains); }
Приведенный выше конвейер операций вернет true , если входная строка содержит все наши ключевые слова.
В качестве альтернативы мы можем просто использовать метод containsAll() фреймворка Collections для достижения желаемого результата:
public static boolean containsWordsArray(String inputString, String[] words) { ListinputStringList = Arrays.asList(inputString.split(" ")); List wordsList = Arrays.asList(words); return inputStringList.containsAll(wordsList); }
Однако этот метод работает только для целых слов. Таким образом, он будет определять ключевые слова только в том случае, если они разделены пробелами в тексте.
7. Использование алгоритма Ахо-Корасика
Проще говоря, алгоритм Aho-Corasick предназначен для поиска текста по нескольким ключевым словам . Он имеет O(n) временную сложность независимо от того, сколько ключевых слов мы ищем или какова длина текста.
Давайте включим зависимость алгоритма Ахо-Корасика в ваш pom.xml :
org.ahocorasick ahocorasick 0.4.0
Во-первых, давайте построим конвейер trie с массивом ключевых слов words . Для этого мы будем использовать структуру данных Trie :
Trie trie = Trie.builder().onlyWholeWords().addKeywords(words).build();
После этого вызовем метод парсера с входной строкой текстом, в котором мы хотели бы найти ключевые слова, и сохраним результаты в коллекции emits :
Collectionemits = 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(); Collectionemits = 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 builder.
Кроме того, имейте в виду, что мы также удаляем повторяющиеся элементы из коллекции emits , так как для одного и того же ключевого слова может быть несколько совпадений.
8. Заключение
В этой статье мы узнали, как найти несколько ключевых слов внутри строки. Кроме того, мы показали примеры с использованием основного JDK, а также с библиотекой Aho-Corasick .
Как обычно, полный код этой статьи доступен на GitHub .