2009-11-12 10 views
96

¿Cuál es la importancia del método Pattern.compile()?
¿Por qué necesito compilar la cadena de expresiones regulares antes de obtener el objeto Matcher?java.util.regex: importancia de Pattern.compile()?

Por ejemplo:

String regex = "((\\S+)\\s*some\\s*"; 

Pattern pattern = Pattern.compile(regex); // why do I need to compile 
Matcher matcher = pattern.matcher(text); 
+2

Bueno, la importancia es casi ninguno si la implementación (como en el JDK 1.7) es sólo una mera ATAJO al nuevo patrón (expresiones regulares, 0); Dicho esto, la REAL importancia no es el método estático en sí, sino la creación y el retorno de un nuevo patrón que se puede guardar para su uso posterior. Tal vez haya otras implementaciones en las que el método estático tome una nueva ruta y guarde en caché los objetos del Patrón, ¡y ese sería un caso real de la importancia de Pattern.compile()! – marcolopes

+0

Las respuestas ponen de relieve la importancia de separar el patrón y combinar las clases (que es probablemente lo que hace la pregunta), pero nadie responde por qué no podemos simplemente utilizar un constructor 'nuevo patrón (regex)' en lugar de una función de compilación estática. marcolopes comment está en el lugar. –

Respuesta

114

El método compile() siempre se llama en algún momento; es la única forma de crear un objeto Pattern. Entonces, la pregunta es, ¿por qué debería llamarlo explícitamente? Una razón es que necesita una referencia al objeto Matcher para que pueda usar sus métodos, como group(int) para recuperar los contenidos de los grupos de captura. La única manera de obtener el objeto Matcher es a través del método matcher() del objeto Pattern, y la única manera de obtener el objeto Pattern es a través del método compile(). Luego está el método find() que, a diferencia de matches(), no está duplicado en las clases String o Pattern.

La otra razón es evitar crear el mismo objeto Pattern una y otra vez. Cada vez que utiliza uno de los métodos de expresión regex en String (o el método estático matches() en Patrón), crea un nuevo Patrón y un nuevo Matcher. Así que este fragmento de código:

for (String s : myStringList) { 
    if (s.matches("\\d+")) { 
     doSomething(); 
    } 
} 

... es exactamente equivalente a esto:

for (String s : myStringList) { 
    if (Pattern.compile("\\d+").matcher(s).matches()) { 
     doSomething(); 
    } 
} 

Obviamente, que está haciendo una gran cantidad de trabajo innecesario. De hecho, puede tardar más tiempo en compilar la expresión regular y crear una instancia del objeto Pattern, que en realizar una coincidencia real. Entonces, por lo general, tiene sentido sacar ese paso del ciclo. Puede crear el Matcher antes de tiempo, así, aunque no son casi tan caro:

Pattern p = Pattern.compile("\\d+"); 
Matcher m = p.matcher(""); 
for (String s : myStringList) { 
    if (m.reset(s).matches()) { 
     doSomething(); 
    } 
} 

Si está familiarizado con expresiones regulares de .NET, puede que se pregunte si el método de Java compile() está relacionado con .NET de RegexOptions.Compiled modificador; la respuesta es no. El método Pattern.compile() de Java es simplemente equivalente al constructor Regex de .NET. Cuando especifica la opción Compiled:

Regex r = new Regex(@"\d+", RegexOptions.Compiled); 

...compila la expresión regular directamente al código de byte CIL, lo que le permite realizar un trabajo mucho más rápido, pero a un costo significativo en el procesamiento inicial y en el uso de la memoria. Piénselo como esteroides para las expresiones regulares. Java no tiene equivalente; no hay diferencia entre un Patrón creado detrás de escena por String#matches(String) y uno que usted crea explícitamente con Pattern#compile(String).

(EDIT:.. Me dijo originalmente que todos los objetos .NET expresiones regulares se almacenan en caché, lo cual es incorrecto Desde .NET 2.0, el almacenamiento en caché automático se produce sólo con métodos estáticos como Regex.Matches(), no cuando se llama a un constructor Regex directamente ref)

+0

Sin embargo, esto no explica la importancia de un método tan trivial en la clase del patrón! Siempre asumí que el método estático Pattern.compile era mucho más que un SHORTCUT simple a un nuevo patrón (regex, 0); Esperaba un CACHE de patrones compilados ... estaba equivocado. Tal vez crear un caché es más costoso que crear nuevos patrones ?? – marcolopes

+7

Tenga en cuenta que la clase Matcher no es segura para subprocesos y no debe compartirse entre subprocesos. Por otro lado Pattern.compile() es. – gswierczynski

+0

TLDR; "... [Pattern.compile (...)] compila la expresión regular directamente al código de bytes CIL, lo que le permite realizar mucho más rápido, pero a un costo significativo en el procesamiento inicial y el uso de la memoria" –

12

Cuando se compila el Pattern Java hace algún cálculo para hacer combinaciones para encontrar en String s más rápido. (Crea una representación en memoria de la expresión regular)

Si va a reutilizar el Pattern varias veces, vería un gran aumento de rendimiento con respecto a la creación de un nuevo Pattern cada vez.

En el caso de utilizar solo el Patrón una vez, el paso de compilación parece una línea de código adicional, pero, de hecho, puede ser muy útil en el caso general.

+2

Por supuesto, puede escribirlo todo en una sola línea 'Matcher matching = Pattern.compile (regex) .matcher (text);'. Esto tiene ventajas con respecto a la introducción de un único método: los argumentos son efectivamente nombrados y es obvio cómo factorizar el 'Patrón' para un mejor rendimiento (o dividir entre métodos). –

+0

Siempre parece que sabes mucho sobre Java. Deben ser contratados para trabajar para ellos ... – jjnguy

+0

@jinguy Compruebe el perfil de Tom – Amarghosh

29

compila analiza la expresión regular y construye una representación en memoria. La sobrecarga para compilar es significativa en comparación con una coincidencia. Si está usando un patrón varias veces obtendrá algún rendimiento para almacenar en caché el patrón compilado.

+4

Plus puede especificar banderas como CASE_INSENSITIVE, dot_all, etc. momento de la elaboración, mediante introducción de un parámetro adicional banderas –

0

pre-compilar la expresión regular aumenta la velocidad. Volver a utilizar el Matcher te da otra ligera aceleración. Si se llama al método con frecuencia, digamos que se llama dentro de un bucle, el rendimiento general ciertamente aumentará.

2

Es cuestión de rendimiento y uso de memoria, compilar y mantener el patrón cumplido si hay que usarla mucho. Un uso típico de regex es para entrada de usuario validado (formato), y también datos de salida de formato para usuarios, en estas clases, guardando el patrón cumplido, parece bastante lógico ya que generalmente llaman mucho.

A continuación se muestra un validador, que en realidad se llama mucho :)

public class AmountValidator { 
    //Accept 123 - 123,456 - 123,345.34 
    private static final String AMOUNT_REGEX="\\d{1,3}(,\\d{3})*(\\.\\d{1,4})?|\\.\\d{1,4}"; 
    //Compile and save the pattern 
    private static final Pattern AMOUNT_PATTERN = Pattern.compile(AMOUNT_REGEX); 


    public boolean validate(String amount){ 

     if (!AMOUNT_PATTERN.matcher(amount).matches()) { 
      return false; 
     }  
     return true; 
    }  
} 

Según lo mencionado por @Alan Moore, si tiene expresiones regulares reutilizable en su código, (antes de un bucle, por ejemplo), se debe compilar y guardar el patrón para su reutilización.

Cuestiones relacionadas