Używając Java Regex, jak sprawdzić, czy łańcuch znaków Zawiera którekolwiek ze słów w zbiorze?

Mam zestaw słów: jabłko, pomarańcza , gruszka, banan, kiwi

Chcę sprawdzić, czy zdanie Zawiera którekolwiek z wyżej wymienionych słów, a jeśli tak, chcę znaleźć, Które słowo pasowało. Jak mogę to osiągnąć w Regex ?

Aktualnie wywołuję String.indexOf() dla każdego z moich zestawów słów. Zakładam, że nie jest to tak skuteczne, jak dopasowanie regex?

Author: user193116, 2012-03-01

3 answers

TL; DR dla prostych podciągów contains() jest najlepszy, ale dla dopasowania tylko całych słów wyrażenia regularne są prawdopodobnie lepsze.

Najlepszym sposobem, aby sprawdzić, która metoda jest bardziej skuteczna, jest przetestowanie jej.

Możesz użyć String.contains() zamiast String.indexOf(), aby uprościć swój kod bez wyrażenia regularnego.

Aby wyszukać różne słowa Wyrażenie regularne wygląda tak:

apple|orange|pear|banana|kiwi

| działa jako OR w wyrażeniach regularnych.

Moje bardzo proste kod testu wygląda tak:

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");
   }
}

Otrzymałem następujące wyniki:

Contains took 5962ms
Regular Expression took 63475ms

Oczywiście czasy będą się różnić w zależności od liczby wyszukiwanych słów i wyszukiwanych ciągów, ale contains() wydaje się być ~10 razy szybsze niż wyrażenia regularne dla prostego wyszukiwania takiego jak to.

Używając wyrażeń regularnych do wyszukiwania ciągów wewnątrz innego ciągu używasz młota kowalskiego, aby złamać orzech, więc chyba nie powinniśmy się dziwić, że jest wolniejszy. Zapisz wyrażenia regularne, gdy wzorce, które chcesz znaleźć, są bardziej złożone.

Jednym z przypadków, w którym możesz używać wyrażeń regularnych, jest sytuacja, gdy indexOf() i contains() nie wykonają zadania, ponieważ chcesz dopasować tylko całe słowa, a nie tylko podciągi, np. chcesz dopasować pear, ale nie spears. Wyrażenia regularne dobrze radzą sobie z tym przypadkiem, ponieważ mają pojęcie granic wyrazów.

W tym przypadku zmienimy nasz wzór na:

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

The \b mówi, aby dopasować tylko początek lub koniec słowa, a nawiasy grupują wyrażenia OR razem.

Uwaga, podczas definiowania tego wzorca w kodzie musisz unikać ukośników za pomocą innego ukośnika:

 Pattern p = Pattern.compile("\\b(apple|orange|pear|banana|kiwi)\\b");
 47
Author: Dave Webb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-01 12:53:29

Myślę, że wyrażenie regularne nie zrobi lepszej pracy pod względem wydajności, ale możesz go użyć w następujący sposób:

Pattern p = Pattern.compile("(apple|orange|pear)");
Matcher m = p.matcher(inputString);
while (m.find()) {
   String matched = m.group(1);
   // Do something
}
 7
Author: Guillaume Polet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-01 11:52:58

Oto najprostsze rozwiązanie, jakie znalazłem (pasujące do symboli wieloznacznych):

boolean a = str.matches(".*\\b(wordA|wordB|wordC|wordD|wordE)\\b.*");
 3
Author: Yanir Calisar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-13 16:37:45