sábado, 7 de febrero de 2015

Expresiones regulares



Las expresiones regulares son el típico ejemplo de las cosas que si no las usan con frecuencia se olvidan y tienes que ir tirando de Google para recordarlas.

Por si no las conocéis todavía, son una gran ayuda a la hora de buscar y reemplazar textos, aunque tienen una pequeña curva de aprendizaje. Una vez superada, no puedes vivir sin ellas.

Tiene soporte en los lenguajes, entornos de desarrollo y en los editores de textos más conocidos.

Podemos practicar las búsquedas con Notepad++,  un editor free source code muy completo.

Una guía de referencia rápida de expresiones regulares que suelo consultar es http://msdn.microsoft.com/es-es/library/az24scfc.aspx

Tienes más info en



Repasemos con ejemplos más útiles.

Caracteres

.
Cualquier carácter que no sea el salto de linea \n
m.sa
La masa está en la mesa de la musa
[chars]
Coincide con cualquier carácter de los especificados
m[ae]sa
La masa está en la mesa de la musa
[^chars]
Cualquier carácter de los que NO estén encerrados por corchete
[^aem]
mesa 18
\t
Caracter de tabulación
\r
Caracter de Retorno de carro (ASCII 13)
\n
Caracter de Nueva Linea (ASCII 10)
Recordad que el salto de línea de Windows es \n\r  (chr 10 + chr 13)
\
Seguido de un carácter, es la forma de representar ese carácter si coincide con símbolos reservados. P.Ej,  \.   \*  \(   \)
\x00
Caracter ascii especificado en hexadecimal.
Por ejemplo, “\x23” seleccionará “#”  (ascii 35)

 

Clases de carácter

\w
Cualquier carácter numérico o letra
\w
P#5.8
\W
Cualquier carácter que no sea numérico o letra
\W
P#5.8
\s
El carácter de espacio en blanco
\S
Cualquier caracter que no sea un espacio en blanco
\d
Cualquier dígito decimal
 
 
a18
\D
Cualquier carácter que no sea un dígito decimal
 
a18

 

 

Cuantificadores

Los cuantificadores añaden cantidad de repeticiones a los caracteres

*
Repetición ninguna o más veces
+
Repetición una o más veces
Ej: Cualquier número que se repita una o más veces
\d+
d189b40h
?
Cero o una vez
Ej: Cualquier cifra, seguida opcionalmente de una letra, y otra cifra
\d\w?\d
1A9  19 1
{n}
Exactamente N veces
Ej de extracción de una fecha: 2 digitos, la barra /, 2 digitos, la barra /, 4 digitos 
\d{2}/\d{2}/\d{4}
El 15/08/2014 llegaré a 2/300
{n,}
Al menos N veces
Elíge las palabras de al menos 3 letras 
\w{3,}
arbol, ale, so
{n,m}
De N a M veces
Ej de extracción de una fecha que puede contener 1 o 2 digitos en días y meses, y 4 digitos en año
\d{1,2}/\d{1,2}/\d{4}
El 15/8/2014 llegaré a 2/300

 

Anclajes

^
Inicio de línea
 
Selecciona la línea entera que empieza por número
^\d.*$
frase 1.
2 frase
Frase 3.
$
Fin de línea
 
Selecciona la última palabra que no acabe en punto
\w+[^.]$  
ó
\w+$
La frase 1.
La 2ª frase
Frase numero 3.
\b
Marca la posición de una palabra limitada por espacios en blanco, puntuación o el inicio/final de una cadena.
 
Ej: Selecciona las palabras que comiencen por vocal
\b[aeiou]\w*\b
encina,pino,
romero,olmo
,almendro,cerezo.

 
 

Greedy and Lazy  (búsquedas tardías y tempranas)

Por defecto, todas estas búsquedas que hemos visto son búsquedas tardías. Es decir, tienden a englobar el mayor número de elementos.
Si hacemos una búsqueda de los tags <span> de un html devolverá del primero al último.

 

<span>.*</span>
Esta es la <span>casa</span> con las mejores <span>vistas</span>

 

Como veis, el modo tardío ha seleccionado desde el primer span al último span, incluyendo el texto del medio.  Si queremos cambiar el comportamiento de tardío (greedy) a tempranp (lazy), usaremos el símbolo ?

<span>.*?</span>
Esta es la  <span>casa</span> con las mejores <span>vistas</span>

 

Grupos

Los grupos se pueden usar para posicionarnos en un punto de la búsqueda sin llegar a seleccionarlo. Es algo duro de entender, pero con un ejemplo se ve mejor.

En el ejemplo anterior, vimos que con la expresión regular <span>.*?</span> seleccionábamos los span, incluyendo los tags. Si lo que queremos es seleccionar el CONTENIDO del span haciendo referencia al propio tag SPAN sin seleccionarlo,  los introduciremos dentro de grupos de búsqueda anterior y posterior.

(?<=expr) posiciona el resultado de la búsqueda tras el resultado de expr, sin llegar a seleccionar la expresión

(?=expr) posiciona el resultado de la búsqueda justo donde comienza la expresión, sin llegar a seleccionarla

Con lo que podemos conseguir seleccionar el contenido de 2 tags html

(?<=<span>).*?(?=</span>)
Esta es la <span>casa</span> con las mejores <span>vistas</span>

 

Con los grupos también podemos usar condicionales, es decir, si se cumple una condición u otra

En este caso, elegiremos entre las marcas de inicio de palabra,  las fechas que sean 1 o 2 cifras, barra, 1 o 2 cifras , barra, y o 2 o 4 cifras

\b\d{1,2}/\d{1,2}/(\d{4}|\d{2})\b
 
El 15/8/2035 llegaré a 2/300
El 3/12/15 llegaré a 2/300
El 3/12/340 llegaré a 2/300

 

Otra característica de los grupos es que los resultados los podemos introducir en variables para tratarlos desde c#.

Un grupo puede definirse con nombre o con índice.

(expr)  Grupo con índice

(?<nombre>expr)  Grupo con nombre de variable

^\w+,\sa\s(?<DIA>\d{1,2})\sde\s
(?<MES>\w+\b)\sde\s(?<AÑO>\d{4})$
Valencia, a 4 de febrero de 2018
Madrid, a 15 de abril de 2019

Traducido “al castellano”, quedaría de la siguiente forma:

“Desde el inicio de la línea, selecciona letras hasta llegar a una coma, un espacio, una “a”, un espacio, 1 o 2 dígitos que los introduces en la variable “DIA”, un espacio, “de”, un espacio, texto que lo introduces en la variable “MES”, un espacio, “de”, un espacio, 4 dígitos que los introduces en la variable “AÑO”

Otro ejemplo de grupos: Extraer  fechas que están en un formato algo mezclado

^\w{2}(?<AÑO>\d{4})(?<MES>\d{2})(?<DIA>\d{2})_
(?<H>\d{2}):(?<M>\d{2}):(?<S>\d{2})\.txt$
DW20141128_08:59:48.txt
UP20141115_09:39:46.txt
DW20141106_11:45:20.txt

 

Pasado a c#, este ejemplo se quedaría de la siguiente manera.




 
Ejemplos para validaciones
 


IBAN (España)
ES\d{2}[ ]\d{4}[ ]\d{4}[ ]\d{4}[ ]\d{4}[ ]\d{4}|ES\d{22}
Codigo postal
^([1-9]{2}|[0-9][1-9]|[1-9][0-9])[0-9]{3}$
Número
telefono (España)
^[0-9]{2,3}-? ?[0-9]{6,7}$
Visa
 
^((67\d{2})|(4\d{3})|(5[1-5]\d{2})|(6011))(-?\s?\d{4}){3}|(3[4,7])\ d{2}-?\s?\d{6}-?\s?\d{5}$
Contraseña segura
(?!^[0-9]*$)(?!^[a-zA-Z]*$)^([a-zA-Z0-9]{8,10})$
URL
^(ht|f)tp(s?)\:\/\/[0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*(:(0-9)*)*(\/?)( [a-zA-Z0-9\-\.\?\,\'\/\\\+&amp;%\$#_]*)?$
Correo electrónico
 
^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(( [a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$

 

Conclusión

Las expresiones regulares, como habéis podido comprobar, es una herramienta muy potente que nos puede sacar de muchos apuros y ahorrarnos docenas de bucles infernales para trastear con textos.

 

Espero que os sea de provecho.