Expresiones regulares en JavaScript
En esta lección
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 hagastexto.match(...).lengthsin comprobarlo antes. Usatexto.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:
| Clase | Significa |
|---|---|
[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 |
\d | una cifra (igual que [0-9]) |
\w | letra, cifra o _ (solo inglés: sin tildes ni ñ) |
\s | un espacio, tabulador o salto de línea |
\D, \W, \S | lo contrario de las anteriores |
. | cualquier carácter salvo el salto de línea |
Cuidado:
\wno 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:
| Cuantificador | Veces | Ejemplo | Coincide 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):
| Flag | Nombre | Efecto |
|---|---|---|
g | global | Busca todas las coincidencias, no solo la primera |
i | ignore case | No distingue mayúsculas de minúsculas |
m | multiline | ^ y $ valen para cada línea, no solo para el texto entero |
u | unicode | Trata bien emojis y permite \p{L} |
s | dotAll | El . 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,startsWithosplit("-"), úsalos. - No intentes validar HTML, JSON o fechas reales con una regex: usa
DOMParser,JSON.parseoDate. - 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")estrue. - No escapar los especiales:
/1.5/acepta “1x5”; escribe/1\.5/. - No comprobar el
nulldematch:texto.match(/x/g).lengthrompe si no hay coincidencias. - Reutilizar una regex con
gentest: guarda la posición de la última búsqueda enlastIndex, 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.
replaceAllcon regex sing: lanzaTypeError.- Esperar que
\wacepte tildes o ñ: no lo hace; usa\p{L}con el flagu.
Resumen
| Quiero… | Uso |
|---|---|
| Saber si un texto cumple el patrón | regex.test(texto) |
| La primera coincidencia (y sus grupos) | texto.match(regex) |
| Todas las coincidencias | texto.match(/.../g) |
| Todas, con grupos y posiciones | texto.matchAll(/.../g) |
| Reemplazar | texto.replace(regex, "$1") o con función |
| Trocear por varios separadores | texto.split(/[,;\s]+/) |
| Pieza | Significado |
|---|---|
\d \w \s . | cifra, carácter de palabra, espacio, cualquiera |
[...] [^...] | uno del conjunto / ninguno del conjunto |
? * + {n,m} | cuántas veces |
^ $ \b | principio, final, límite de palabra |
( ) (?: ) (?<n> ) | grupo, sin captura, con nombre |
| | alternativa |
g i m u s | flags |
Pruébalo tú
Cambia el código y pulsa Ejecutar (o Ctrl + Enter).
Pon a prueba lo que has aprendido
¿Te ha quedado claro? Márcala y verás tu progreso en el explorador.