Introducción a las expresiones regulares en JavaScript

Ejemplo de la complejidad de una expresión regular

Las expresiones regulares en JavaScript y en cualquier lenguaje son un recurso muy importante que todo programador empezará a usar más temprano que tarde. Porque aunque todos los problemas no se pueden resolver con expresiones regulares, si que hay muchos donde esta técnica nos ayudará. Es verdad que antes de empezar a usarlas asustan un poco, porque de entrada ya vemos un revoltijo de caracteres que parecen imposibles de leer. Estos temas pretenden un acercamiento a las expresiones regulares y que al final no resulten tan "ásperas".

La mejor forma de estudiar las expresiones regulares es practicar con ellas. La herramienta Probador de Expresiones Regulares nos servirá de ayuda. Se trata de una aplicación para buscar cadenas en un texto. Podemos poner los patrones y texto que iremos explicando en temas siguientes para observar el resultado. Las coincidencias encontradas serán resaltadas con dos colores alternativos rojo y azul cuando el modificador global esté activado. Si no lo estuviera sólo resaltaría la primera coincidencia en rojo. Cuando se hace una búsqueda se ofrece información relevante sobre algunas propiedades de la ejecución de la expresión regular.

En este primer tema haremos una introducción a las expresiones regulares. En uno siguiente explicaremos la sintaxis, pasando luego a ver los métodos de ejecución en JavaScript. Por último haremos un ejercicio para crear un patrón de expresión regular que nos permita validar si una dirección de email está bien escrita.

Declaraciones de expresiones regulares de JavaScript

Una expresión regular en JavaScript es un objeto que contiene un patrón de expresión regular. A veces confundimos estos dos términos por lo que intentaré aclarar qué es cada cosa. Una declaración de una expresión regular válida podría ser como la que sigue:

var  reg = /.../gmi;
    

El patrón es todo lo que está encerrado entre las barras, en este caso los tres puntos consecutivos. No son tres puntos suspensivos cualesquiera, puesto que como veremos más abajo, ciertos caracteres como este punto tienen un significado especial en el patrón. En este caso el punto busca cualquier caracter y ese patrón buscará tres caracteres seguidos. Opcionalmente el patrón se finaliza con uno o más modificadores. Estos son letras "g", "m" e "i" en cualquier orden. Por lo tanto ya estamos diferenciando entre patrón y sus modificadores y la propia expresión regular resultante de la declaración, la variable reg, que no es otra cosa que un objeto de JavaScript.

Realmente la anterior declaración es una forma abreviada de la instanciación del objeto global window.RegExp:

var reg = new RegExp("...", "gmi");
    

Como con otros métodos y propiedades del objeto window, podemos prescindir de su referencia. Esta segunda forma tiene la ventaja de poder declarar expresiones regulares dinámicamente, pues el primer argumento es una cadena de texto para el patrón y el segundo argumento opcional son los modificadores. En definitiva decimos que creamos una expresión regular cuando declaramos una instancia de RegExp.

Captura de pantalla del Developer Tools de un navegador con un punto de interrupción en el código JavaScript para mostrar las propiedades y métodos de una variable.
Figura. Métodos exec() y test() de un objeto de expresión regular en JavaScript.

Pero una vez creada tenemos que ejecutarla, para lo cual disponemos de otros métodos que usan esas expresiones regulares. Por un lado tenemos los métodos de String como texto.match(reg), texto.search(reg), texto.split(reg, limite) y texto.replace(reg, reemplazo). Se aplican por lo tanto a una cadena de texto, siendo el argumento reg una expresión regular. Por otro lado tenemos los métodos del propio objeto de expresión regular (ver Figura), que son reg.exec(cadena) y reg.test(cadena). También existe un método compile() pero es obsoleto y no debe usarse. Observe como ahora se aplican a una expresión regular, siendo el argumento la cadena sobre la que buscar.

Esto de encontrar los métodos de ejecución en diferentes objetos es algo lioso. Más de una vez tengo que ver si es un método de String o de la expresión regular. Pienso que todos deberían ser métodos de la expresión regular, pero bueno, así están las cosas y así hemos de aprenderlas.

Instanciación de una expresión regular y objeto RegExp

Si declaramos una expresión regular con var reg = new RegExp("...", "gmi"); o bien con la forma literal var reg = /.../gmi;, la variable reg es una instancia de expresión regular del objeto RegExp. Ambos se actualizan tras ejecutar un método. Estos métodos devuelven las coincidencias encontradas, pero también en el objeto RegExp podemos encontrar resultados.

El objeto RegExp contendrá los resultados de la última ejecución que ha ocurrido en la página. Así que si consultamos con RegExp los resultados hemos de hacerlo inmediatamente después de la ejecución de algún método. No hay que olvidar que los métodos de ejecución match() y exec() devuelven los resultados en un array. Además con el método replace() podemos también acceder con las plantillas string $&, $1, $2,... en su argumento replace. O usando arguments en una función de reemplazo. Todo esto lo veremos en un tema posterior sobre los métodos de ejecución. Por lo tanto no hay necesidad de acceder a los resultados a través del RegExp.

Además no se recomienda acceder a los resultados a través de RegExp por una cuestión de seguridad. Pues es díficil controlar si otra expresión regular en nuestro script o, lo que es peor, scripts de terceros pudieran estar también modificando el RegExp después de que nosotros ejecutáramos uno, con lo que suplantarían nuestros resultados. En la página de Mozilla Características obsoletas marca estas propiedades de RegExp como deprecated, lo que significa que aún siguen soportándose pero en un futura no lo serán (pasarán a obsolete).

En la herramienta para probar expresiones regulares encontrará información detallada sobre estos objetos. Con cada ejecución podrá ver que contiene cada uno de ellos con la estructura que se expone a continuación.

Propiedades de la instancia de expresión regular

  • source: Cadena con el patrón sin delimitadores ni modificadores "gmi".
  • global: Booleano indicado el modificador "g", para ejecutar sólo una búsqueda (false) o buscar todas las coincidencias (true).
  • multiline: Booleano con el modificador "m" búsqueda multilínea, donde el comportamiento del patrón puede ser diferente con las anclas ^ y $. Sin multilínea indicarán inicio y final de texto, en otro caso indicarán inicio y final de línea.
  • ignoreCase: Booleano con el modificador "i" de ignorar mayúsculas con valor true. Algo como /abc/i, encontrará "aBc" si está activado.
  • lastIndex: Entero con la posición del carácter donde empezará a buscar después de la primera coincidencia, contando desde cero. En este caso si que es una propiedad de la expresión regular, accediendo con reg.lastIndex.

Propiedades del array de resultados

  • index: Entero con la posición del carácter donde empieza la primera coincidencia, contando desde cero. Es una propiedad del conjunto de resultados, no de la instancia de expresión regular. Por lo tanto está disponible sólo en métodos como exec() que devuelven un array de resultados. Por ejemplo, con
    var reg = /.../g;
    var res = reg.exec(texto);
    tendremos los resultados en el array res. Entonces accedemos a la posición desde el array de resultados con res.index

Propiedades del objeto RegExp

  • input o $_: Texto sobre el que se va a buscar. A algunas propiedades se puede acceder con un formato corto. Así RegExp.input es igual que RegExp.$_, o usando la notación con corchetes RegExp["$_"] como con cualquier propiedad de un objeto.
  • lastMatch o $&: Última coincidencia del patrón completo. Cuando la búsqueda no es global ("g"), sólo habrá una coincidencia. En otros lenguajes esta referencia puede ser $0 pero en JavaScript $0 no significa nada.
  • $1 a $9: Coincidencia en el paréntesis o grupo de captura 1 a 9. Se ordenan desde la izquierda a la derecha y desde el exterior al interior. Con el método replace() podemos incluir estos grupos de capturas como plantillas string.
  • lastParen o $+: Último paréntesis encontrado.
  • leftContext o $`: Porción de texto desde el inicio hasta el carácter inmediatamente anterior a la última coincidencia.
  • rightContext o $': Porción de texto inmediatamente después de la última coincidencia y hasta el final del texto.
  • index: Posición del carácter donde empieza la primera coincidencia, contando desde cero (sólo para IE).
  • lastIndex: Posición del carácter donde empezará a buscar después de la primera coincidencia, contando desde cero (sólo para IE).

A excepción de las propiedades index y lastIndex los navegadores Chrome, Firefox e Internet Explorer se comportan igual. Con esas propiedades hay algunas diferencias, pues son soportadas también por IE en el RegExp. De todas formas ya comenté que no es buena cosa obtener los resultados a partir de este objeto. Pero si lo hace tenga en cuenta que lo navegadores podrían dejar de soportarlo, como sucede con IE9 y posteriores al menos cuando accedemos dentro de un función de reemplazo en el método replace().