Menu button

Parte 1: Expresiones regulares para Frontend

Las expresiones regulares suelen estar olvidadas por la gran mayoría de Frontend, imagino que algunos motivos pueden ser su complejidad y las pocas veces que se necesitan. Con esta serie de artículos pretendo otorgarles la importáncia que merecen explicando todo lo que un Frontend debe conocer sobre ellas.

En esta primera parte, pretendo explicar qué son, para qué se utilizan y cómo funcionan.

Qué son las expresiones regulares

Se suele definir una expresión regular como:

Una expresión regular es una especificación declarativa que describe la estructura textual a la que debe corresponder una cadena de coincidencia

O, dentro del contexto de Google Analytics, también como:

Las expresiones regulares son secuencias específicas de caracteres que coinciden con patrones de sus datos de Analytics o con parte de estos.

Sin embargo, las expresiones regulares no son declarativas, ni describen, ni especifican una estructura. Tampoco son cadenas con las que deba coincidir nada.

Las expresiones regulares son código. Al escribir una expresión regular escribimos un "programa", en un lenguaje un poco peculiar, pero al fin y al cabo estamos programando. Por lo tanto, ésta es una definicióm más concreta de lo que son las empresiones regulares:

Una expresión regular es una especificación imperativa de una secuencia de bloques de instrucciones estructuradas, diseñada para ejecutar alguna tarea en una máquina virtual altamente especializada. Las expresiones regulares son secuencias estructuradas de instrucciones, bucles, control de flujo, aserciones y gestión de excepciones.

Una expresion regular también es conocida por regex o RegExp.

Para qué se utilizan

Utilizamos expresiones regulares para evitar tener que escribir código en Javascript (aunque también en C, C#, Perl, PHP, Python o el lenguaje que utilizemos).

Utilizar una RegExp es el equivalente a llamar a una función, ya que a partir de un argumento -la cadena que debe coincidir- se ejecutan sus instrucciones (la regex) y recogemos el resultado, que puede ser un booleano o una serie de resultados como grupo de coincidencias.

Dialectos

Las regex no son un único lenguaje, son toda una familia de lenguajes o dialectos y cada uno de ellos tiene sus peculiaridades y suelen ser incompatibles entre sí. Todos utilizan las mismas instrucciones, con significados completamente distintos.

Los dialectos más importantes son:

  • BRE: PHP, ed, sed, grep.
  • ERE: egrep, awk, Tcl
  • EMACS: emacs
  • VIM: vim
  • PCRE: PHP, Perl, Javascript (parcialmente), apache, Google Analytics (un subconjunto muy reducido), C#, java, python...
  • PSIX: Perl 6

Por lo tanto, tenemos que determinar qué lenguaje habla nuestro entorno, que en este caso es PCRE para Javascript.

El modelo de ejecución

En el modelo de ejecución de una regex simple como puede ser /abc/, vemos tres instrucciones:

  1. coincide con una a
  2. a continuación coincide con una b
  3. a continuación coincide con una c

En cada instrucción hay una excepción implícita, coincide o lanza excepción, en ese caso, hace backtrack y continúa con el siguiente carácter.

Por lo tanto, podemos representar la regex /abc/ como si de una maquina de estados finita (FSM) se tratase mediante el siguiente gráfico:

Modelo de ejecución de una regex

Modelo de ejecución de una regex haciendo match

Ejemplo de ejecución

Dada la cadena de texto 01ababc:

Ejemplo de ejecución de una regex

¡Y así es como funciona una expresión regular!

Las instrucciones

Cada carácter en una regex es una instrucción y por norma general, o coincide con este carácter o hace backtrack.

Habitualmente tenemos que presentar a una regex una serie de alternativas u opciones, por ejemplo /abc|abx/. En este caso, tenemos dos posibles caminos, coincide con una a, una b y una c o con una a, una b y una x.

Es muy importante conocer que toda RegExp:

  • Prueba todos los caminos posibles empezando por la izquierda.
  • Intenta hacer el mínimo backtracking posible.
  • Si hace match, termina.
  • Si falla, reintenta con la siguiente posición.
  • Si fallan todos los caminos, en cada reintento, reporta fallo.

Además, las RegExp no son muy listas... El motor intenta siempre todos los caminos posibles antes de avanzar por el texto y siempre encuentra el primero de los posibles substrings que coincide en vez de la mejor coincidencia, por lo que esto tiene algunas consecuencias.

Ante este escenario:

  • Cadena: "Siempre utilizo el mail de gmail"
  • RegExp: /gmail|mail/

El resultado es: "Siempre utilizo el mail de gmail"

Por lo que, una expresión regular siempre encuentra la primera y más corta coincidencia y eso hace que sea muy fácil cometer errores de éste estilo:

  • Cadena: "He perdido la fotografía"
  • RegExp: /foto|fotografía/

Para evitarlo, invertid siempre el orden de las keywords en la regex.

  • Cadena: "He perdido la fotografía"
  • RegExp: /fotografía|foto/

⚠️ Las expresiones regulares que fallan suelen ser mucho más lentas que las que coinciden. Y recordad algo muy importante, las expresiones regulares tienen la manía de hacer lo que les decimos que hagan, no lo que queremos que hagan.