Подтвердить что ты не робот

Использование Java Regex, как проверить, содержит ли строка какое-либо из слов в наборе?

У меня есть набор слов: яблоко, апельсин, груша, банан, киви

Я хочу проверить, содержит ли предложение какое-либо из перечисленных выше слов, и если это так, я хочу найти, какое слово соответствует. Как я могу выполнить это в Regex?

В настоящее время я вызываю String.indexOf() для каждого из моих наборов слов. Я предполагаю, что это не так эффективно, как сопоставление регулярных выражений?

4b9b3361

Ответ 1

TL; DR Для простых подстрок contains() лучше всего, но только для соответствия целым словам. Правильное выражение, вероятно, лучше.

Лучший способ увидеть, какой метод более эффективен, - проверить его.

Вы можете использовать String.contains() вместо String.indexOf(), чтобы упростить код, не относящийся к регулярному выражению.

Для поиска разных слов регулярное выражение выглядит следующим образом:

apple|orange|pear|banana|kiwi

| работает как OR в регулярных выражениях.

Мой очень простой тестовый код выглядит следующим образом:

public class TestContains {

   private static String containsWord(Set<String> words,String sentence) {
     for (String word : words) {
       if (sentence.contains(word)) {
         return word;
       }
     }

     return null;
   }

   private static String matchesPattern(Pattern p,String sentence) {
     Matcher m = p.matcher(sentence);

     if (m.find()) {
       return m.group();
     }

     return null;
   }

   public static void main(String[] args) {
     Set<String> words = new HashSet<String>();
     words.add("apple");
     words.add("orange");
     words.add("pear");
     words.add("banana");
     words.add("kiwi");

     Pattern p = Pattern.compile("apple|orange|pear|banana|kiwi");

     String noMatch = "The quick brown fox jumps over the lazy dog.";
     String startMatch = "An apple is nice";
     String endMatch = "This is a longer sentence with the match for our fruit at the end: kiwi";

     long start = System.currentTimeMillis();
     int iterations = 10000000;

     for (int i = 0; i < iterations; i++) {
       containsWord(words, noMatch);
       containsWord(words, startMatch);
       containsWord(words, endMatch);
     }

     System.out.println("Contains took " + (System.currentTimeMillis() - start) + "ms");
     start = System.currentTimeMillis();

     for (int i = 0; i < iterations; i++) {
       matchesPattern(p,noMatch);
       matchesPattern(p,startMatch);
       matchesPattern(p,endMatch);
     }

     System.out.println("Regular Expression took " + (System.currentTimeMillis() - start) + "ms");
   }
}

Результаты, которые я получил, были следующими:

Contains took 5962ms
Regular Expression took 63475ms

Очевидно, что тайминги будут различаться в зависимости от количества искомых слов и искомых строк, но contains(), кажется, в ~ 10 раз быстрее, чем регулярные выражения для простого поиска, подобного этому.

Используя регулярные выражения для поиска строк внутри другой строки, вы используете кувалду, чтобы взломать гайку, поэтому я думаю, мы не должны удивляться, что она медленнее. Сохранять регулярные выражения, когда шаблоны, которые вы хотите найти, более сложны.

В одном случае, когда вы можете использовать регулярные выражения, если indexOf() и contains() не будут выполнять задание, потому что вы хотите только совместить целые слова, а не подстроки, например. вы хотите совместить pear, но не spears. Регулярные выражения хорошо справляются с этим случаем, так как они имеют концепцию границ слов.

В этом случае мы изменили бы наш шаблон на:

\b(apple|orange|pear|banana|kiwi)\b

\b говорит, что соответствует только началу или концу слова, а скобки группируют выражения OR вместе.

Обратите внимание, что при определении этого шаблона в коде вам нужно избежать обратных косых черт с помощью другой обратной косой черты:

 Pattern p = Pattern.compile("\\b(apple|orange|pear|banana|kiwi)\\b");

Ответ 2

Я не думаю, что регулярное выражение будет лучше работать с точки зрения производительности, но вы можете использовать его следующим образом:

Pattern p = Pattern.compile("(apple|orange|pear)");
Matcher m = p.matcher(inputString);
while (m.find()) {
   String matched = m.group(1);
   // Do something
}

Ответ 3

Вот самое простое решение, которое я нашел (сопоставление с подстановочными знаками):

boolean a = str.matches(".*\\b(wordA|wordB|wordC|wordD|wordE)\\b.*");