2012-03-01 22 views
32

Tengo un conjunto de palabras, por ejemplo, manzana, naranja, pera, plátano, kiwiUsando Java Regex, ¿cómo comprobar si una cadena contiene alguna de las palabras de un conjunto?

Quiero comprobar si una oración contiene alguna de las palabras enumeradas anteriormente, y si lo hace, quiero encontrar la palabra emparejado ¿Cómo puedo lograr esto en Regex?

Actualmente estoy llamando a String.indexOf() para cada uno de mis conjuntos de palabras. ¿Asumo que esto no es tan eficiente como una coincidencia de expresiones regulares?

Respuesta

47

TL; DR Para subcadenas simples es mejor, pero para solo las palabras completas de Expresión regular son probablemente mejores.

La mejor manera de ver qué método es más eficaz es probarlo.

Puede usar String.contains() en lugar de String.indexOf() para simplificar su código no regexp.

Para buscar diferentes palabras con la expresión regular se ve así:

apple|orange|pear|banana|kiwi 

Los | trabajos como OR en las expresiones regulares.

Mi muy simple código de prueba es el siguiente:

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

Los resultados que obtuve fueron los siguientes:

Contains took 5962ms 
Regular Expression took 63475ms 

Obviamente los tiempos pueden variar en función del número de palabras que se buscaron y la Las cadenas que se buscan, pero parece ser ~ 10 veces más rápido que las expresiones regulares para una búsqueda simple como esta.

Al usar Expresiones regulares para buscar Cuerdas dentro de otra Cadena, está utilizando una maza para romper una nuez, así que supongo que no debería sorprendernos que sea más lenta. Guarde expresiones regulares para cuando los patrones que desea encontrar son más complejos.

Un caso en el que es posible que desee utilizar expresiones regulares es si indexOf() y no va a hacer el trabajo porque solo deseas para que coincida con las palabras enteras y no sólo subseries, por ejemplo Quiere hacer coincidir pear pero no spears. Las expresiones regulares manejan bien este caso ya que tienen el concepto de word boundaries.

En este caso nos gustaría cambiar nuestro patrón a:

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

El \b dice que sólo se corresponde con el principio o el final de una palabra y el grupo entre paréntesis el o expresiones juntos.

Nota, al definir este patrón en su código que necesita para escapar de las barras invertidas con otra barra invertida:

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

No creo que una expresión regular va a hacer un mejor trabajo en términos de rendimiento, pero se puede utilizar de la siguiente manera:

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

¿No puedes leer? Nunca dije que fuera eficiente. –

+1

El rendimiento depende de la longitud de la expresión regular. Si tiene menos de 1000 caracteres, continúe. Si es más tiempo, necesitas otra solución. Por ejemplo, divida el texto para separar las palabras y compruébelas con la tabla hash predefinida/conjunto de palabras "conocidas". – AlexR

+2

@deporter el propósito de la respuesta es dar una buena pista sobre cómo resolver la pregunta para no proporcionar una solución perfecta, brillante y de clase mundial. Se puede mejorar fácilmente y, en cuanto a legibilidad, si tiene 200 cadenas (una razón más para no usar regexp para eso), puede usar un for-loop y concatenar en un 'StringBuilder'. Creo que mi respuesta proporciona suficiente sabor. –

2

Aquí está la solución más sencilla que encontré (coincidente con comodines):

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