web-dev-qa-db-fra.com

En utilisant Java Regex, comment vérifier si une chaîne contient l'un des mots d'un ensemble?

J'ai un ensemble de mots à dire - pomme, orange, poire, banane, kiwi

Je veux vérifier si une phrase contient l'un des mots énumérés ci-dessus, et si c'est le cas, je veux trouver quel mot correspond. Comment puis-je accomplir cela dans Regex?

J'appelle actuellement String.indexOf () pour chacun de mes mots. Je suppose que ce n'est pas aussi efficace qu'une correspondance regex?

35
user193116

TL; DR Pour les sous-chaînes simples, contains() est préférable, mais pour ne faire correspondre que des mots entiers, l'expression régulière est probablement meilleure.

La meilleure façon de voir quelle méthode est la plus efficace est de la tester.

Vous pouvez utiliser String.contains() au lieu de String.indexOf() pour simplifier votre code non regexp.

Pour rechercher différents mots, l'expression régulière ressemble à ceci:

Apple|orange|pear|banana|kiwi

Le | Fonctionne comme un OR dans les expressions régulières.

Mon code de test très simple ressemble à ceci:

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

Les résultats que j'ai obtenus étaient les suivants:

Contains took 5962ms
Regular Expression took 63475ms

De toute évidence, les délais varient en fonction du nombre de mots recherchés et des chaînes recherchées, mais contains() semble être ~ 10 fois plus rapide que les expressions régulières pour une recherche simple comme celle-ci.

En utilisant des expressions régulières pour rechercher des chaînes dans une autre chaîne, vous utilisez un marteau pour casser un écrou, donc je suppose que nous ne devrions pas être surpris que ce soit plus lent. Enregistrez les expressions régulières lorsque les motifs que vous souhaitez rechercher sont plus complexes.

Un cas où vous voudrez peut-être utiliser des expressions régulières est si indexOf() et contains() ne fera pas le travail parce que vous ne voulez faire correspondre que des mots entiers et pas seulement des sous-chaînes, par exemple vous voulez faire correspondre pear mais pas spears. Les expressions régulières gèrent bien ce cas car elles ont le concept de limites du mot .

Dans ce cas, nous changerions notre modèle en:

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

Le \b Dit de ne faire correspondre que le début ou la fin d'un mot et les crochets regroupent les expressions OR ensemble.

Remarque, lorsque vous définissez ce modèle dans votre code, vous devez échapper aux barres obliques inverses avec une autre barre oblique inverse:

 Pattern p = Pattern.compile("\\b(Apple|orange|pear|banana|kiwi)\\b");
48
Dave Webb

Je ne pense pas qu'une expression rationnelle fera un meilleur travail en termes de performances, mais vous pouvez l'utiliser comme suit:

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

Voici la solution la plus simple que j'ai trouvée (correspondant aux caractères génériques):

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