¿Qué son las expresiones regulares?
Una expresión regular es una forma de definir una 'plantilla', con una sintaxis propia, para localizar un patrón dentro de un texto. El ejemplo más sencillo sería definir una palabra como plantilla:
Encontraría la palabra casa en el texto:
La utilidad real se muestra cuando utilizamos caracteres especiales para crear patrones de busqueda mucho más complejos, del tipo:
Parece mucho más complicado de lo que es en realidad. Sólo hay que aprender unas reglas básicas y unos cuantos caracteres especiales.
JavaScript tiene dos notaciones
En JavaScript hay dos formas de crear una expresión regular: mediante la notación literal o utilizando el constructor RegExp():
//notación literal var myRegExp = /casa/g; /* /reg. expression/flags(g|i|m) */ //Usando el constructor RegExp var myRegExp = new RegExp('casa','g'); /* RegExp('expression','flags(g|i|m)'); */
Las dos son equivalentes. La primera se compila cuando el script se carga y es más rápida. La segunda se compila en tiempo de ejecución por lo que es la única opción si la expresión contiene variables que tienen que ser interpoladas. En este tutorial utilizaremos la notación literal por simplicidad. Todas las explicaciones son válidas también para el constructor.
Para entender mejor el funcionamiento de las expresiones regulares conviene pensar en una busqueda caracter a caracter. Si tenemos el patrón 'casa', lo que estamos buscando no es la palabra casa, sino una 'c' seguida de una 'a' seguida de una 's', etc. Es mucho más facil entender el uso de los caracteres especiales de esta manera.
Por ejemplo, si sabemos que \s representa un espacio en blanco y que ? significa que el caracter anterior es opcional, ¿ que estamos seleccionando en la siguiente expresión regular?:
var myRegex = /\sglobos?/;
Estamos buscando un espacio en blanco, seguido de una 'g', de una 'l', de una 'o', de una 'b', de una 'o' y de una 's' que es opcional, puede aparecer o no. Por tanto seleccionaremos:
Podemos ver que sólo ha seleccionado el primer 'globo'. Por defecto la búsqueda termina cuando se encuentra la primera coincidencia. Si queremos que siga buscando tenemos que utilizar el flag g.
Flags (g, i, m)
Los flags modifican el comportamiento por defecto de la búsqueda. Aparecen justo al lado de la barra que marca el final de la expresión regular y pueden ser:
- g (global). Se busca siempre en el texto completo en vez de detenerse cuando encuentra la primera coincidencia con el patrón.
- i (ignora Mayusculas). No diferencia mayúsculas y minúsculas.
- m (multilínea). El texto incluye saltos de línea. ^ y $ se aplican a 'comienzo de línea' y 'final de línea', en vez de 'comienzo de texto' y 'final de texto'.
Si en la cadena de nuestro ejemplo queremos seleccionar también otras ocurrencias de la palabra 'globo', tenemos que utilizar:
var myRegex = /globo/g;
La expresión encontraría dos coincidencias. El tercer 'Globo' no coincide con el patrón porque la G es mayúscula. Si añadimos el modificador i sí se seleccionará:
var myRegex = /globo/gi;
La segunda y tercera aparición están en plural. Sería bueno que nuestra expresión regular capturara tanto globo como globos. Como ya vimos antes, podemos utilizar la ? para indicar que el carácter anterior es opcional:
var myRegex = /globos?/gi;
?, + , * Cuantificadores sobre el carácter anterior
Con estos tres caracteres extraños podemos indicar que el carácter inmediatamente anterior puede ser opcional, o que tiene que aparecer como mínimo una vez o que puede aparecer muchas veces.
? | El carácter anterior es opcional. Aparece 0 ó 1 veces. |
+ | El carácter anterior aparece una vez o más. |
* | El carácter anterior puede no aparecer, aparecer una vez o aparecer repetido muchas veces (aparece 0 o más veces). |
Si tenemos el texto:
"Tengo un gloooooobo grande y tres globos pequeños."
¿Como podemos hacer para capturar los dos globos?. La expresión anterior no nos sirve porque la primera aparición quedaría fuera.
Necesitamos una expresión regular que nos seleccione cualquier patrón con una 'g' seguida de una 'l', seguida de una o más 'o', una 'b', una 'l', una 'o' y una 's' opcional.
var myRegex = /glo+bos?/gi;
En este caso, si ponemos * en vez de + también funcionaría. La diferencia es que el asterisco permite que el carácter no aparezca ( cero o más veces). Por lo tanto también seleccionaría glbo. Con + obligamos a que el carácter aparezca al menos una vez.
Para seleccionar todas las palabras en el siguiente texto:
"bo, boooo, b."
sólo nos sirve el *:
var myRegex = /bo*/g;
'.' Un comodín para un solo car.cter
El punto '.' es un comodín que puede sustituir a cualquier carácter excepto a un salto de línea.
La siguiente expresión:
var myRegex = /gat./gi;
capturará 'gat' seguido de cualquier carácter:
En las dos últimas coincidencias podemos ver que también captura el espacio en blanco y el punto final.
{n,m} Cuantificadores más concretos
Con {n,m} podemos indicar exactamente el número de veces que aparece el carácter anterior o delimitarlo en un rango concreto ( por ejemplo que aparezca entre 3 y 5 veces).
{n} | El carácter anterior aparece exactamente *n* veces. |
{n,} | El carácter anterior aparece *n* o más veces. |
{n,m} | El carácter anterior aparece un mínimo de *n* y un máximo de *m* veces |
var myRegex = /r{2}/gi;
En el último caso tenemos brrrr. Se seleccionan las dos primeras y despues las dos segundas.
var myRegex = /r{2,}/gi;
var myRegex = /r{2,3}/gi;
[abc] Conjuntos de caracteres
Para indicar que en un lugar de nuestra expresión puede aparecer cualquier de los caracteres de un conjunto concreto podemos utilizar [ ]. Por ejemplo, si queremos seleccionar 'sal', 'cal' y 'mal', podemos indicar que el primer caracter puede ser [scm]. Es decir, puede ser uno de los caracteres del conjunto:
var myRegex = /[scm]al/gi;
También podemos negar el conjunto con el carácter ^, indicando que puede aparecer cualquier carácter menos los que están en el conjunto.
var myRegex = /[^scm]al/gi;
Se puede especificar un rango mediante un guión: [0-7] indica números del 0 al 7, [a-z] indica letras de la 'a' a la 'z'
'^' y '$' Principio y final de línea
Muchas veces es necesario seleccionar una cadena sólamente si está al principio o al final de un texto. Por ejemplo, es muy común eliminar los espacios en blanco que puedan aparecer al principio o al final.
Sabemos que el símbolo \s selecciona un espacio en blanco. Con la siguiente expresión seleccionariamos todos los espacios al principio de un texto:
var myRegex = /^\s+/g;
Estamos buscando un 'principio de texto' seguido de uno ó mas espacios en blanco:
Si queremos seleccionar los del final:
var myRegex = /\s+$/g;
Estamos buscando uno ó mas espacios en blanco justo antes de un final de texto:
En el apartado de flags vimos que existe uno para modificar el comportamiento de estos dos carácteres. El flag 'm', cuando está presente indica que el texto es multilínea y queremos que ^ y $ seleccionen principio y final de cada línea, en vez de principio y final de texto.
\s, \d, \D, \w, \W .... Símbolos para hacer la vida más facil
Algunos conjuntos de caracteres son de uso tan común que se han creado unos símbolos para incluirlos más fácilmente. Por ejemplo, para indicar que un carácter debe ser un número podemos escribir [0-9] o podemos utilizar \d (digit) que es mucho más sencillo. Cuando el símbolo aparece en mayúscula significa exactamente lo contrario. \D sería cualquier carácter que NO sea un número.
Los más utilizados son:
\s | Cualquier tipo de espacio en blanco ( espacio, tabulador, salto de línea, etc) |
\S | Todo carácter que NO sea un espacio en blanco |
\t | Tabulador |
\w | Cualquier carácter alfanumérico. Equivalente a [a-zA-Z0-9_] |
\W | Cualquier carácter NO alfanumérico |
\d | Un dígito. Equivale a [0-9] |
\D | Cualquier carácter que NO sea una dígito |
\b | 'Word Boundary'. Marca el inicio o el fin de una palabra ( similar a '^' y '$' para una línea) |
Como ejemplo podemos escribir una expresión para comprobar un nombre de usuario. Suponemos que el nombre de usuario no puede contener espacios y sólo puede contener números y letras (incluimos tambien el guión bajo o subrayado). Tiene que tener un mínimo de 6 carácteres y un máximo de 12:
var myRegex = /^\w{6,12}$/g;
Sólo seleccionará un nombre de usuario cuando cumpla las condiciones. En caso contrario no seleccionará nada. Puede utilizarse en una condición de JavaScript para aceptarlo o rechazarlo.
'|' OR
La barra vertical nos permite definir varias expresiones diferentes para buscar una o otra:
var myRegex = /perro|gato/gi;
Se busca la coincidencia con la expresión de la de la derecha o de la de la izquierda completas. Si queremos que el OR sólo afecte a una parte de la expresión, tenemos que encerrar esa parte entre paréntesis. Por ejemplo, si queremos buscar 'elefante' y 'elemento'
var myRegex = /ele(mento|fante)/gi;
Tambien podemos definir más de dos opciones:
var myRegex = /ele(mento|fante|gante)/gi;
() Creando subexpresiones en nuestra regex
Los paréntesis se utilizan para marcar una parte de la expresión sobre la que actúa algún operador. En el ejemplo anterior vimos que podemos delimitar el rango sobre el que actuaba OR para que no se aplicara a la expresión completa. También podemos extender el rango sobre el que actuan los cuantificadores.
var myRegex = /1(23)+/gi;
En la expresión anterior el '+' se aplica al grupo completo '23'.
Sin los paréntesis los cuantificadores se aplican sólo al carácter inmediatamente anterior:
var myRegex = /123+/gi;
Los paréntesis tienen también otra función: memorizan todas las coincidencias que se encuentren en el texto y pueden utilizarse despues como $1, $2, $3, etc. Sólo se memoriza y guarda (en $n) la parte de la expresión marcada con los paréntesis. Por ejemplo, en una expresión que seleccione fechas, podemos encerrar entre parentesis la parte que coge el dia y el mes y luego cambirlo de orden colocando $2-$1.
Podemos utilizar ?: para evitar que se memorize el contenido:
var myRegex = /1(?:23)+/gi;
Esto es todo por ahora. En otro tutorial veremos las funciones que tenemos disponibles para utilizar las expresiones regulares de varias formas en JavaScript.
Fuentes: MDN Regular Expressions
Excelente!! muchas gracias
ResponderEliminarNo soy de comentar nunca pero nunca en un blog. De hecho, es mi primer comentario en uno trabajando hace 4 años en sistemas. La verdad, la explicacion que diste sobre el tema es excelente y no me quedo ninguna duda de nada, y eso que sabia expresiones regulares muy poco (casi nada). No solo diste la teoria, sino como comprenderlo. Excelente es poco. Muchas gracias
ResponderEliminarMuy buena explicación con ejemplos simples. Muchas gracias.
ResponderEliminar