Expresiones Regulares explicado para niños





A todo el mundo le da pereza las expresiones regulares, (a mi al que mas), pero bueno vamos a quitarnos el miedo y voy a poner aqui unos ejemplillos sencillos , que luego van creciendo en complejidad (no mucha) pero para que veamos que no son para tanto si te las cuentan bien desde el principio:


Tenemos una cadena de texto a procesar :

String CHAIN = "abcde  %%~~paco123@gmail.comAAbd%%%";

Asi de primeras vemos mucha morralla, vemos un email y simbolos raros...
Vamos a ir testeando expresiones regulares contra esa cadena y asi vamos viendo como podemos ir jugando con ellas y anidandolas

El codigo Java para probarlas  es este:
   
        List<String> regExps = Arrays.asList("\\d", "\\d+", etc.....);
        Pattern p;
        Matcher m;
        List<String> matches = new ArrayList<>();;
        for(String regExp: regExps) {
            matches.clear();
            m = Pattern.compile(regExp).matcher(CHAIN);
            while(m.find()) {
                matches.add(m.group());
            }
            //log.info("{} -> {}", regExp, matches);
            System.out.println(String.format("%s -> %s", regExp, matches));
        }

si nos fijamos en el codigo, cuando una expresion regular se compila y se ejecuta contra una cadena de texto, lo que vamos a ir encontrando son eventos m.find(). Es decir,  internamente va a ir revisandose la cadena caracter a caracter y cuando encuentra algo m.find()->return true y el metodo m.group() nos devuelve lo que acaba de encontrar que cumple nuestra expresion regular.
Por lo tanto el procesamiento es orientado a eventos.



Empezamos de las mas sencillas para arriba :

Usamos los parentesis para meter dentro nuetras expressiones regulares. Te encuenta que una expresion regular compleja puede estar compuesta por varias subexpresiones dentro de parentesis.


(\\d)   output ->  [1, 2, 3]
Esta expresion saca uno por uno todos los digitos
Evalua caracter a caracter y si es un digito salta el find() y nos devuelve un grupo que contiene ese digito. Por lo tanto tenemos en la lista final de ocurrencias 3 entradas, con los numeros 1,2,3.

(\\d+) output -> [123]

Esta expresion es igual va a ir caracter a caracter y si es un numero lo guarda como macheo, pero al tener un "+" le indicamos que si depues de ese digito hay mas digitos que los guarde todos en el mismo grupo, por eso encuentra el 1 y mira el caracter que hay despues , si es otro numero, lo junta con el anterior (12), sigue mirando y encuentra el 3 que tambien machea asi que ya tiene un grupo con 123, como el siguiente caracter es un @ pues da por cerrado el grupo ya que no hay mas coincidencias, entonces la llamada a m.group() devuelve "123".  ¿a que explicado asi es mas sencillo?


(\\w) output -> [a, b, c, d, e, p, a, c, o, 1, 2, 3, g, m, a, i, l, c, o, m, A, A, b, d]

Esta es igual que la de los digitos, pero con caracteres alfanumericos y underscore. Como no tiene + va a ir sacando caracter a caracter. Si te fijas ha excluido los espacios y lo simbolos "raros" ~% .


(\\w+) output-> [abcde, paco123, gmail, comAAbd]


Prima hermana de la anterior, pero como lleva el +,cuando encuentra una coincidencia comienza a almacenar en el grupo, y mete en el mismo grupo hasta que no se cumple la condicion. "abcde" se para porque encuentra un espacio asi que no añade mas a a ese grupo y lo devuelve. sigue mirando y lo siguiente es "p" y sigue almacenando "paco123" porque cumple el patron y cuando llega a la @ lo da por cerrado porque no machea.






([a-z]) output-> [a, b, c, d, e, p, a, c, o, g, m, a, i, l, c, o, m, b, d]


Esta especifica un rango de valores, case sensitive. Por eso hace grupos individuales (no tiene el +) con cada caracter que encuentra dentro del rango de las minusculas. si quieres haz la prueba con ([a-m]) y asi veras que excluye las letras posteriores a la m. Mola!, ya puedes ir jugando con algo.

([a-z]+) output-> [abcde, paco, gmail, com, bd]
Esta es la misma que antes, pero agrupa mientras se cumpla el patron.


([a-zA-Z]+) output-> [abcde, paco, gmail, comAAbd]
Esta indica varios grupos de coincidencias, o de la a-z o de la A-Z y ademas me los agrupas si estan seguidos. ¿a que parecia compleja y no era para tanto?


(paco|pedro|com |gmail) output-> [paco, gmail]
Esta especifica un or de un grupò de valores. si es alguno de esos, pues hace un grupo.

(\\d+{3}) output-> [123]
Esta es un poco mas especial, dice que saque los digitos y que los agrupe, pero que los grupos no los haga mas grandes de 3 caracteres. En el ejemplo encuentra el 123 despues de "paco". si la cadena donde busca fuera "paco4567jose12", lo que nos devolveria solo "456" porque son digitos y los agrupa de 3 en 3, ya que luego esta "jose" que no le vale y por ultimo encuentra "12" pero que como no llega a ser un grupo de 3, pues lo descarta.

 VAMOS A ANIDARLAS

(paco)(\\d) output-> [paco1]
 Esta evalua la primera expresion que encuentra entre parentesis, asi que busca a piñon fijo coincidencias con "paco" y si lo encuentra evalua si despues hay un digito (\\d).  En caso de que se cumplan las 2 expresiones nos devuelve un grupo que conteine "paco1".

(paco)(\\d+) -> [paco123]
Prima hermana de la anterior, pero si te fijas al poner el + en la expresion de los digitos, pues sigue pillando digitos hasta que no se cumpla y nos devuelve todo el chorizo junto "paco123".   "Coño, pues tiene sentido, mola".

(paco)^(\\d) output -> []
Esta mola porque usa la negacion "^". Vemos que busca que tenga "paco" y que despues NO contenga un digito, por eso no devuelve ninguna coincidencia, porque despues de "paco" hay un "1". y entonces lo descarta todo.

([a-zA-Z0-9]+) output -> [abcde, paco123, gmail, comAAbd]
Esta busca que el caracter este entre los margenes a-z o A-Z o 0-9, y si hay varias coincidencias seguidas, que las agrupe y me las devuelva como si fuera un "todo".

([a-zA-Z]+)(\\d+) output -> [paco123]
Lo mismo, pero despues de agrupar tiene que mirar si hay digitos y si hay varios digitos seguidos que lo agrupe todo.

([a-zA-Z]+)(\\d+)@ output -> [paco123@]
Lo mismo que la anterior pero ademas que despues aparezca un @.  Si te fijas , esta expresion lleva ya 3 condiciones (la de las letras, la de los numeros y que justo despues exista un arroba. 


([a-zA-Z]+)(\\d+)@([a-z]+) output -> [paco123@gmail]
Lo mismo, pero que ademas, despue del arroba haya una (o varias) minusculas.

([a-zA-Z]+)(\\d+)@([a-z]+). output -> [paco123@gmail.]
Lo mismo que la anterior y que despues de las minusculas haya un punto "."

([a-zA-Z]+)(\\d+)@([a-z]+).([a-z]{3}) output -> [paco123@gmail.com]
Lo mismo pero que despues del punto haya minusculas y que coga solo 3.

PERO.... SI ACABAMOS DE HACER UNA EXPRESION REGULAR QUE BUSCA EMAILS VALIDOS EN UN CHORIZO DE TEXTO!!!   Y CASI SIN DESPEINARNOS!!

Espero que te haya servido este articulo para entender un poco como funciona y perderle el miedo a las expresiones regulares.

Puedes encontrar estos ejemplos funcionando en mi github:

 RegularExpressionsJavaTest

Comentarios

Entradas populares de este blog

Reiniciar usuario de SVN Subversion

Subir campos Blob a BBDD (Oracle) con Java

Reproducir mp3 con Java