viernes, 1 de junio de 2012

Java Regex

votar
   Vamos a tratar en esta entrada las expresiones regulares en Java. Será sólo una aproximación, porque esto de las expresiones regulares, regex para abreviar, es un lenguaje en sí mismo y da para varios libros. Las expresiones regulares no se utilizan sólo en Java, si no también en otros lenguajes de programación, como Pearl o Groovy, y por supuesto las puedes usar en Word o Writer. Sin embargo, en cada lenguaje existen ligeras diferencias a la hora de usarlas. Como si no fuera ya bastante complicado el tema. Lo que sí tienen en común es que se usan para encontrar y manipular texto.

Una expresión regular sustituye desde un simple carácter a oraciones enteras que deseas encontrar en un texto. Evidentemente, si quieres encontrar más de una palabra, o incluso una palabra larga, no es muy práctico tener que escribirlas literalmente.

Java tiene dos clases, dentro del paquete java.util.regex, especializadas en este tipo de manipulación de texto: Matcher y Pattern. Pattern compila una expresión regular para crear un patrón. Matcher utiliza dicho patrón para hallar coincidencias en el texto.
Por ejemplo:

String texto = “Este es el texto en el que vamos a buscar un patrón”;
String patrónDeTexto = “.*el.*”; //.* significa cero o más caracteres antes y después de la palabra.
Pattern patrón = Pattern.compile(patrónDeTexto); //creamos una instancia de Pattern
Matcher match = patrón.matcher(texto); //creamos una instancia de Matcher
boolean b = match.matches();
System.out.println(“Coincide: “+ b);

Si la expresión regular sólo la vamos a usar una vez, podemos utilizar el método matches() con Pattern sin necesidad de una instancia de Matcher, simplificando lo anterior así:

String texto = “Este es el texo en el que vamos a buscar un patrón”;
String patrónDeTexto = “.*el.*”;
boolean b = Pattern.matches(patrónDeTexto, texto);
System.out.println(“Coincide: “+b);

lo que nos daría la misma salida que en el caso anterior:
Coincide: true


Supongamos ahora que queremos saber exáctamente cuántas coincidencias hay en el texto de la palabra “el” y en qué lugar se encuentran. Para ello utilizaremos los métodos de Matcher find(), start() y end(), teniendo en cuenta que el índice comienza en cero y que los espacios en blanco también cuentan como caracteres.

String texto = “Este es el texto en el que vamos a buscar un patrón”;
String patrónDeTexto = “el”; //queremos saber cuántas veces aparece esta palabra.
Pattern patrón = Pattern.compile(patrónDeTexto);
Matcher match = patrón.matcher(texto);
int contador = 0;
while (match.find()){
contador++;
System.out.println(“Coincidencia: “ + contador + “ de ” + match.start() + “ a ” + matcher.end());
}

La salida en este caso será la siguiente:
Coincidencia: 1 de 8 a 10
Coincidencia: 2 de 20 a 22

Otro método interesante es split(), utilizado para separar el texto en distintas líneas, de esta manera:

String texto = “Este es el texto, en el que vamos a buscar, un patrón”);
String patrónDeTexto = “,”;
Pattern patrón = Pattern.compile(patrónDeTexto);
String[] split = patrón.split(texto);
for(String palabras : split){
System.out.println(palabras);
}

La salida en este caso es:
Este es el texto
en el que vamos a buscar
un patrón

Hasta ahora hemos visto unos cuantos métodos de las clases Pattern y Matcher, pero poco sobre las expresiones regulares en sí. Pero como ya hemos dicho, utilizar expresiones literales suele ser muy inconveniente. Veamos ahora algunas de las expresiones regulares más utilizadas en Java.

.    equivale a cualquier carácter
\d equivale a cualquier dígito
\D equivale a cualquier carácter no dígito
\s equivale a un espacio en blanco
\S equivale a cualquier carácter excepto un espacio en blanco
\w equivale a cualquier letra
\W equivale a cualquier carácter excepto letras
[abc] equivale a a, b o c (o cualquier otro carácter que se indique)
[^abc] equivale a cualquier carácter excepto a, b o c
[a-zA-Z] equivale a cualquier carácter de la a a la z o de la A a la Z, todos inclusive
[a-d[m-p]] equivale a cualquier carácter de la a la d o de la m a la p
[a-z&&[def]] equivale a d, e o f
[a-z&&[^bc]] equivale a cualquier carácter de la a a la z excepto b o c
[a-z&&[^m-p]] equivale a cualquier carácter de la a a la z excepto los que van de la m a la p


Además, en java regex se utilizan cuantificadores para indicar el número de veces que se repiten los caracteres buscados. Estos cuantificadores se denominan greedy, reluctant y possessive. Para indicar el cuantificador greedy, siendo X un carácter cualquiera, se escribe:
X* para buscar coincidencias de cero o más X
X? para buscar coincidencias de una o ninguna X
X+ para buscar coincidencias de una o más X

Para denotar el cuantificador reluctant, a los signos anteriores se añade ?, de esta manera:
X*?
X??
X+?
Y para el cuantificador possessive, el signo +:
X*+
X?+
X++

¿Cómo funcionan estos cuantificadores? Bueno, el cuantificador greedy “lee” todo el texto de una vez y luego, de derecha a izquierda, se va deshaciendo de caracteres y vuelve a leer lo que queda una y otra vez hasta encontrar una coincidencia. El cuantificador reluctant, en cambio, empieza leyendo el texto de iquierda a derecha hasta hallar la coincidencia buscada. El cuantificador possessive lee el texto entero como el greedy, pero solo una vez, y si no haya una coincidencia esa vez, no sigue buscando más.

Veamos esto con un ejemplo. Supongamos que tenemos el siguiente texto “ID usuario Nombre usuario” y buscamos cero o más coincidencias de la siguiente expresión regular .io(cualquier carácter seguido de io)
Greedy: .*io. Leería el texto entero, que no está seguido de io, e iría “soltando” caracteres hasta que encontrase ID usuario Nombre usuar, seguido de los caracteres io que habría soltado y encontraría una coincidencia:
ID usuario Nombre usuario.
Reluctant: .*?io. Iría leyendo el texto de izquierda a derecha y encontaría dos coincidencias:
ID usuario
Nombre usuario
Possessive: .*+io. Leería todo el texto, pero solo una vez, y claro, como el texto no está seguido de io, no encontaría ninguna coincidencia.

Como hemos dicho, esto solo es una breve aproximación a las expresiones regulares en Java, pero esperamos que os sirva para ir practicándolas, así como a entender cómo funcionan las clases Pattern y Matcher.

No hay comentarios:

Publicar un comentario en la entrada