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