Saltar al contenido
expresiones-regulares.js · devschool

Expresiones regulares en JavaScript

Lección 13 de 38 · 10 min de lectura · Actualizado el

En esta lección
  1. Crear una expresión regular
  2. Los métodos para usarlas
  3. Las piezas del patrón
  4. Flags
  5. Ejemplos prácticos
  6. No te pases con las regex
  7. Errores frecuentes
  8. Resumen

Una expresión regular (o regex) es un patrón que describe cómo es un texto: “cinco cifras seguidas”, “algo, una arroba, algo, un punto y algo más”, “una palabra que empieza por mayúscula”. Con ella puedes comprobar si un texto tiene un formato, buscar trozos dentro de él o reemplazarlos. Es la herramienta que hay detrás de la validación de formularios, de los buscadores de los editores de código y de muchas limpiezas de datos. Parecen jeroglíficos al principio, pero se leen símbolo a símbolo y con unas pocas piezas ya resuelves la mayoría de casos.

Si todavía no dominas los métodos de texto normales (includes, slice, split…), repasa antes cadenas de texto: muchas veces no necesitas una regex.

Crear una expresión regular

En JavaScript una regex se escribe entre barras, con flags opcionales detrás:

const patron = /gato/;      // busca "gato"
const sinMayus = /gato/i;   // igual, pero sin distinguir mayúsculas

También se puede crear con el constructor RegExp, útil cuando el patrón sale de una variable:

const nombre = "ana";
const exacto = new RegExp("^" + nombre + "$", "i");
exacto.test("ANA"); // true

La forma con barras es la habitual. Usa new RegExp solo cuando el patrón se construye en tiempo de ejecución.

Los métodos para usarlas

test: ¿cumple el patrón?

Es un método de la regex y devuelve true o false. Es el que usarás para validar:

const codigoPostal = /^\d{5}$/;
codigoPostal.test("39001");  // true
codigoPostal.test("3900");   // false
codigoPostal.test("39001a"); // false

match: ¿qué trozo coincide?

Es un método del texto. Sin flag g devuelve la primera coincidencia con información extra; con g, un array con todas; si no encuentra nada, null:

const texto = "Pedido 1024: 3 tazas y 12 libros";

texto.match(/\d+/);      // [ '1024', index: 7, ... ]
texto.match(/\d+/g);     // [ '1024', '3', '12' ]
"sin números".match(/\d+/g); // null

Cuidado: como puede devolver null, no hagas texto.match(...).length sin comprobarlo antes. Usa texto.match(...) ?? [].

matchAll: todas las coincidencias con detalle

Cuando necesitas todas las coincidencias y sus grupos o posiciones, usa matchAll (exige el flag g) y recórrelo con for...of:

const ticket = "Café 1,50 €, tostada 2,20 € y zumo 3 €";

for (const r of ticket.matchAll(/(\d+(?:,\d+)?) €/g)) {
  console.log(r[1], "en la posición", r.index);
}
// 1,50 en la posición 5
// 2,20 en la posición 21
// 3 en la posición 35

replace y replaceAll

Reemplazan lo que coincide. Con una regex sin g, replace cambia solo la primera coincidencia; con g, todas:

"gato perro gato".replace(/gato/, "loro");  // "loro perro gato"
"gato perro gato".replace(/gato/g, "loro"); // "loro perro loro"

replaceAll con una regex obliga a poner g; si no, lanza TypeError. El segundo argumento puede ser también una función que recibe cada coincidencia y devuelve el reemplazo:

"hola mundo".replace(/\b\w/g, (letra) => letra.toUpperCase()); // "Hola Mundo"
"Precio: 20 euros".replace(/\d+/, (n) => n * 2);               // "Precio: 40 euros"

split con regex

split acepta una regex como separador. Ideal cuando los datos vienen separados de varias maneras:

"uno  dos,tres ; cuatro".split(/[\s,;]+/);
// [ 'uno', 'dos', 'tres', 'cuatro' ]

Las piezas del patrón

Caracteres literales y especiales

Las letras y números se buscan tal cual: /hola/ busca “hola”. Pero algunos caracteres tienen significado especial: . * + ? ^ $ ( ) [ ] { } | \ /. Para buscarlos literalmente, escápalos con \:

"1x5".match(/\d.\d/);   // [ '1x5' ]  el punto significa "cualquier carácter"
"1x5".match(/\d\.\d/);  // null       ahora busca un punto de verdad

Clases de caracteres

Una clase representa un solo carácter de un conjunto:

ClaseSignifica
[abc]a, b o c
[a-z]una letra minúscula de la a a la z
[0-9]una cifra
[^0-9]cualquier cosa que no sea una cifra
\duna cifra (igual que [0-9])
\wletra, cifra o _ (solo inglés: sin tildes ni ñ)
\sun espacio, tabulador o salto de línea
\D, \W, \Slo contrario de las anteriores
.cualquier carácter salvo el salto de línea

Cuidado: \w no reconoce la ñ ni las vocales con tilde. "año".match(/\w+/) devuelve solo "a". Para letras de cualquier idioma usa /\p{L}+/u.

Cuantificadores

Dicen cuántas veces se repite lo anterior:

CuantificadorVecesEjemploCoincide con
?0 o 1/colou?r/color, colour
*0 o más/ab*/a, ab, abbb
+1 o más/\d+/7, 42, 2026
{n}exactamente n/\d{5}/39001
{n,m}entre n y m/\d{2,4}/12, 123, 1234
{n,}n o más/\w{8,}/8 caracteres o más

Los cuantificadores son codiciosos: cogen todo lo que pueden. Añadiendo ? detrás se vuelven perezosos y cogen lo mínimo:

const html = "<b>x</b><b>y</b>";
html.match(/<b>.*<\/b>/)[0];  // "<b>x</b><b>y</b>" (todo)
html.match(/<b>.*?<\/b>/)[0]; // "<b>x</b>"         (lo mínimo)

Anclas

No representan caracteres, sino posiciones:

  • ^: principio del texto.
  • $: final del texto.
  • \b: límite de palabra.

Las anclas son la diferencia entre “contiene” y “es exactamente”. Sin ellas, /\d{5}/ acepta "abc39001xyz". Para validar, rodea siempre el patrón con ^ y $.

Alternancia y grupos

| significa “o”: /gato|perro/ encuentra cualquiera de las dos palabras. Los paréntesis agrupan y, además, capturan lo que coincide para usarlo luego:

const fecha = "2026-09-30";
const m = fecha.match(/(\d{4})-(\d{2})-(\d{2})/);
console.log(m[0], m[1], m[2], m[3]); // 2026-09-30 2026 09 30

En replace, los grupos se usan con $1, $2…:

fecha.replace(/(\d{4})-(\d{2})-(\d{2})/, "$3/$2/$1"); // "30/09/2026"

Si solo quieres agrupar sin capturar, usa (?:...), como en el ejemplo del ticket: (?:,\d+)? significa “opcionalmente, una coma y cifras”.

Grupos con nombre

Contar paréntesis es incómodo. Con (?<nombre>...) cada grupo tiene nombre y el código se lee mucho mejor:

const patron = /(?<anio>\d{4})-(?<mes>\d{2})-(?<dia>\d{2})/;
const { anio, mes, dia } = "2026-09-30".match(patron).groups;
console.log(anio, mes, dia); // 2026 09 30

"2026-09-30".replace(patron, "$<dia>/$<mes>/$<anio>"); // "30/09/2026"

Flags

Van detrás de la barra final y se pueden combinar (/patron/gi):

FlagNombreEfecto
gglobalBusca todas las coincidencias, no solo la primera
iignore caseNo distingue mayúsculas de minúsculas
mmultiline^ y $ valen para cada línea, no solo para el texto entero
uunicodeTrata bien emojis y permite \p{L}
sdotAllEl . también coincide con el salto de línea
const notas = "Ana: 8\nLuis: 4\nEva: 6";
notas.match(/^\w+/gm);        // [ 'Ana', 'Luis', 'Eva' ]

/^a.b$/.test("a\nb");         // false
/^a.b$/s.test("a\nb");        // true

Ejemplos prácticos

Código postal español

Cinco cifras, y las dos primeras son la provincia (01 a 52):

const cp = /^(0[1-9]|[1-4]\d|5[0-2])\d{3}$/;
cp.test("39001"); // true
cp.test("28080"); // true
cp.test("53000"); // false: no existe la provincia 53
cp.test("00123"); // false

Formato de DNI

Ocho cifras y una letra mayúscula. La regex comprueba el formato; la letra correcta se calcula con el resto de dividir entre 23:

function dniValido(dni) {
  const m = dni.toUpperCase().match(/^(\d{8})([A-Z])$/);
  if (!m) return false;
  const letras = "TRWAGMYFPDXBNJZSQVHLCKE";
  return letras[Number(m[1]) % 23] === m[2];
}

dniValido("12345678Z"); // true
dniValido("12345678A"); // false: formato bueno, letra mala

Este ejemplo enseña algo importante: la regex valida la forma, no el significado. Para lo demás, código normal.

Email sencillo

const email = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
email.test("ana@ejemplo.com");  // true
email.test("ana@ejemplo");      // false
email.test("ana @ejemplo.com"); // false

Se lee: “uno o más caracteres que no sean espacio ni arroba, una arroba, más de lo mismo, un punto y más de lo mismo”. No es perfecta, y no hace falta: la única forma de saber si un email existe es mandarle un mensaje. En formularios, además, <input type="email"> ya lo valida (lo ves en formularios).

Teléfono español

Nueve cifras que empiezan por 6, 7, 8 o 9, con prefijo +34 y espacios opcionales:

const tel = /^(\+34\s?)?[6789]\d{2}\s?\d{3}\s?\d{3}$/;
tel.test("612345678");       // true
tel.test("+34 612 345 678"); // true
tel.test("512345678");       // false
tel.test("61234567");        // false: faltan cifras

Fíjate en \+: el + hay que escaparlo porque es un cuantificador.

No te pases con las regex

Las expresiones regulares son potentes, pero una regex de 80 caracteres no la entiende nadie, ni tú dentro de un mes.

  • Si basta con includes, startsWith o split("-"), úsalos.
  • No intentes validar HTML, JSON o fechas reales con una regex: usa DOMParser, JSON.parse o Date.
  • Divide el problema: una regex para el formato y código normal para las reglas (como en el DNI).
  • Deja un comentario que explique qué acepta el patrón.

Para escribirlas y probarlas, usa regex101.com con el sabor ECMAScript (JavaScript): explica cada símbolo, marca las coincidencias en vivo y te deja probar muchos textos a la vez.

Errores frecuentes

  • Olvidar ^ y $ al validar: /\d{5}/.test("abc123456") es true.
  • No escapar los especiales: /1.5/ acepta “1x5”; escribe /1\.5/.
  • No comprobar el null de match: texto.match(/x/g).length rompe si no hay coincidencias.
  • Reutilizar una regex con g en test: guarda la posición de la última búsqueda en lastIndex, y la siguiente llamada sigue desde ahí.
const conG = /a/g;
conG.test("aa"); // true
conG.test("aa"); // true
conG.test("aa"); // false (!) había llegado al final

Para test, no uses g.

  • replaceAll con regex sin g: lanza TypeError.
  • Esperar que \w acepte tildes o ñ: no lo hace; usa \p{L} con el flag u.

Resumen

Quiero…Uso
Saber si un texto cumple el patrónregex.test(texto)
La primera coincidencia (y sus grupos)texto.match(regex)
Todas las coincidenciastexto.match(/.../g)
Todas, con grupos y posicionestexto.matchAll(/.../g)
Reemplazartexto.replace(regex, "$1") o con función
Trocear por varios separadorestexto.split(/[,;\s]+/)
PiezaSignificado
\d \w \s .cifra, carácter de palabra, espacio, cualquiera
[...] [^...]uno del conjunto / ninguno del conjunto
? * + {n,m}cuántas veces
^ $ \bprincipio, final, límite de palabra
( ) (?: ) (?<n> )grupo, sin captura, con nombre
|alternativa
g i m u sflags

Pruébalo tú

Cambia el código y pulsa Ejecutar (o Ctrl + Enter).

expresiones-regulares.js
Consola

Pon a prueba lo que has aprendido

[JavaScript] ¿Qué muestra la consola?
console.log(/\d{3}/.test("tel: 12345"));

[JavaScript] ¿Qué muestra la consola?
console.log("a1b22c333".match(/\d+/g));

[JavaScript] ¿Qué devuelve "2026-09-30".replace(/(\d{4})-(\d{2})-(\d{2})/, "$3/$2/$1")?

[JavaScript] ¿Qué muestra la consola?
const r = /a/g;
console.log(r.test("ana"), r.test("ana"), r.test("ana"));

¿Te ha quedado claro? Márcala y verás tu progreso en el explorador.