CAP 14 · LEC 02·Bonus prácticos
Expresiones regulares: buscar y transformar texto con patrones
Las expresiones regulares son patrones para buscar, validar y transformar texto. Son intimidantes al principio, pero dominando su sintaxis básica puedes resolver problemas complejos de texto en pocas líneas.
¿Encontraste un error o algo que mejorar?Editá esta lección en GitHub →
¿Qué son las expresiones regulares?
Una expresión regular (regex) es un patrón que describe un conjunto de strings. En JavaScript se escriben entre barras /patron/ o con new RegExp("patron").
// Dos formas de crear una regex
const patron1 = /hola/; // literal (preferido)
const patron2 = new RegExp("hola"); // constructor (cuando el patrón es dinámico)
// Flags más comunes:
// i → case-insensitive (ignora mayúsculas)
// g → global (encuentra todas las coincidencias)
// m → multiline (^ y $ aplican a cada línea)
const texto = "Hola mundo, hola JavaScript!";
console.log(/hola/.test(texto)); // false — case sensitive
console.log(/hola/i.test(texto)); // true — ignora mayúsculas
const coincidencias = texto.match(/hola/gi);
console.log(coincidencias); // ["Hola", "hola"]
// Regex con patrones dinámicos
function buscarPalabra(texto, palabra) {
const patron = new RegExp(`\\b${palabra}\\b`, "gi"); // \b = word boundary
return texto.match(patron) || [];
}
console.log(buscarPalabra("El gato y el gatobus", "gato")); // ["gato"] — solo la palabra exacta// TypeScript no tiene tipos especiales para regex — usa RegExp
const patron: RegExp = /hola/gi;
const texto: string = "Hola mundo, hola JavaScript!";
// test() retorna boolean
const encontrado: boolean = patron.test(texto);
console.log(encontrado); // true (o false si ya se usó el flag g con lastIndex)
// Resetear lastIndex para regex con flag g
patron.lastIndex = 0;
// match() retorna string[] | null
const coincidencias: RegExpMatchArray | null = texto.match(/hola/gi);
console.log(coincidencias); // ["Hola", "hola"]
// Función reutilizable tipada
function buscarPalabra(texto: string, palabra: string): string[] {
const patron = new RegExp(`\\b${palabra}\\b`, "gi");
return texto.match(patron) ?? [];
}
console.log(buscarPalabra("El gato y el gatobus", "gato")); // ["gato"]Salida
false
true
["Hola", "hola"]
["gato"]Sintaxis básica — clases de caracteres
Los metacaracteres son los bloques de construcción de las regex:
// Clases de caracteres predefinidas
// \d — dígito [0-9]
// \D — no dígito
// \w — palabra [a-zA-Z0-9_]
// \W — no palabra
// \s — espacio en blanco (espacio, tab, newline)
// \S — no espacio
// . — cualquier carácter (excepto newline)
const texto = "Teléfono: +52 55 1234-5678";
const soloDigitos = texto.match(/\d/g);
console.log(soloDigitos?.join("")); // "52551234567 8" → "525512345678"
// Anclas
// ^ — inicio del string (o línea con flag m)
// $ — fin del string (o línea con flag m)
console.log(/^\d/.test("123abc")); // true — empieza con dígito
console.log(/^\d/.test("abc123")); // false — empieza con letra
console.log(/\d$/.test("abc123")); // true — termina con dígito
// Clases personalizadas con []
const vocales = /[aeiouáéíóúü]/gi;
console.log("Hola Mundo".match(vocales)); // ["o", "a", "u", "o"]
// Rango
const letrasAC = /[a-c]/gi;
console.log("abcdefg".match(letrasAC)); // ["a", "b", "c"]
// Negación con ^
const noVocales = /[^aeiou]/gi;
console.log("Hola".match(noVocales)); // ["H", "l"]// Metacaracteres en TypeScript — misma sintaxis
const telefono: string = "+52 55 1234-5678";
// Extraer solo dígitos
const soloDigitos: string[] = telefono.match(/\d/g) ?? [];
console.log(soloDigitos.join("")); // "52551234 5678"
// Función de validación con regex
function soloLetras(texto: string): boolean {
return /^[a-záéíóúüñs]+$/i.test(texto);
}
console.log(soloLetras("Ana García")); // true
console.log(soloLetras("Ana123")); // false
// Clase personalizada para slugs válidos
function esSlugValido(slug: string): boolean {
return /^[a-z0-9]+(-[a-z0-9]+)*$/.test(slug);
}
console.log(esSlugValido("mi-primer-curso")); // true
console.log(esSlugValido("Mi Curso")); // false
console.log(esSlugValido("curso-")); // falseSalida
525512345678
true
false
true
["o", "a", "u", "o"]
true
falseCuantificadores — ¿cuántas veces?
Los cuantificadores controlan cuántas veces debe aparecer un elemento:
// * → 0 o más veces
// + → 1 o más veces
// ? → 0 o 1 vez (opcional)
// {n} → exactamente n veces
// {n,} → n o más veces
// {n,m} → entre n y m veces
// Número de teléfono mexicano: 10 dígitos
const telRegex = /^\d{10}$/;
console.log(telRegex.test("5512345678")); // true
console.log(telRegex.test("551234567")); // false — solo 9
// Código postal: 5 dígitos
const cpRegex = /^\d{5}$/;
console.log(cpRegex.test("06600")); // true
console.log(cpRegex.test("6600")); // false
// Contraseña: mínimo 8 caracteres, al menos una mayúscula y un número
const passRegex = /^(?=.*[A-Z])(?=.*\d).{8,}$/;
console.log(passRegex.test("Abc12345")); // true
console.log(passRegex.test("abc12345")); // false — sin mayúscula
console.log(passRegex.test("Abcdefgh")); // false — sin número
// Cuantificador lazy (?) — mínimo posible
const html = "<b>hola</b> y <b>mundo</b>";
const greedy = html.match(/<b>.+<\/b>/g); // ["<b>hola</b> y <b>mundo</b>"]
const lazy = html.match(/<b>.+?<\/b>/g); // ["<b>hola</b>", "<b>mundo</b>"]
console.log(greedy);
console.log(lazy);// Validaciones comunes con cuantificadores
function validarTelefono(tel: string): boolean {
// Acepta: 5512345678 o +52 55 1234-5678
return /^(\+52\s?)?\d{2}\s?\d{4}[-\s]?\d{4}$/.test(tel);
}
console.log(validarTelefono("5512345678")); // true
console.log(validarTelefono("+52 55 1234-5678")); // true
console.log(validarTelefono("123")); // false
function validarContrasena(pass: string): { valida: boolean; errores: string[] } {
const errores: string[] = [];
if (pass.length < 8) errores.push("Mínimo 8 caracteres");
if (!/[A-Z]/.test(pass)) errores.push("Al menos una mayúscula");
if (!/\d/.test(pass)) errores.push("Al menos un número");
if (!/[!@#$%]/.test(pass)) errores.push("Al menos un símbolo (!@#$%)");
return { valida: errores.length === 0, errores };
}
const resultado = validarContrasena("Abc12345!");
console.log(resultado.valida); // true
console.log(resultado.errores); // []Salida
true
false
true
true
false
["<b>hola</b> y <b>mundo</b>"]
["<b>hola</b>", "<b>mundo</b>"]Grupos y capturas — extraer partes del texto
Los grupos () capturan partes del match para usarlas por separado. Los grupos no capturantes (?:) agrupan sin capturar.
// Grupos numerados — se acceden por índice
const fechaRegex = /(\d{4})-(\d{2})-(\d{2})/;
const match = "Fecha: 2026-05-02".match(fechaRegex);
if (match) {
console.log(match[0]); // "2026-05-02" — match completo
console.log(match[1]); // "2026" — grupo 1
console.log(match[2]); // "05" — grupo 2
console.log(match[3]); // "02" — grupo 3
}
// Grupos nombrados — más legibles
const fechaNombrada = /(?<anio>\d{4})-(?<mes>\d{2})-(?<dia>\d{2})/;
const matchNombrado = "2026-05-02".match(fechaNombrada);
if (matchNombrado?.groups) {
const { anio, mes, dia } = matchNombrado.groups;
console.log(`Año: ${anio}, Mes: ${mes}, Día: ${dia}`);
// "Año: 2026, Mes: 05, Día: 02"
}
// Grupo no capturante (?:) — agrupar sin capturar
const urlRegex = /(?:https?|ftp):\/\/([\w.-]+)/;
const urlMatch = "visita https://ejemplo.com/ruta".match(urlRegex);
console.log(urlMatch?.[1]); // "ejemplo.com" — solo el dominio// Grupos nombrados en TypeScript
const fechaNombrada: RegExp = /(?<anio>\d{4})-(?<mes>\d{2})-(?<dia>\d{2})/;
function parsearFecha(fechaStr: string): { anio: string; mes: string; dia: string } | null {
const match = fechaStr.match(fechaNombrada);
if (!match?.groups) return null;
const { anio, mes, dia } = match.groups;
return { anio, mes, dia };
}
const resultado = parsearFecha("2026-05-02");
console.log(resultado); // { anio: "2026", mes: "05", dia: "02" }
// Extraer partes de una URL
function parsearURL(url: string): { protocolo: string; dominio: string } | null {
const match = url.match(/(?<protocolo>https?|ftp):\/\/(?<dominio>[\w.-]+)/);
if (!match?.groups) return null;
return {
protocolo: match.groups.protocolo,
dominio: match.groups.dominio,
};
}
console.log(parsearURL("https://ejemplo.com/ruta"));
// { protocolo: "https", dominio: "ejemplo.com" }Salida
2026-05-02
2026
05
02
Año: 2026, Mes: 05, Día: 02
ejemplo.comMétodos de string con regex
Los strings tienen métodos que trabajan directamente con regex:
// test() — ¿coincide? → boolean
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
console.log(emailRegex.test("ana@ejemplo.com")); // true
console.log(emailRegex.test("no-es-email")); // false
// match() — encontrar coincidencias → array | null
const texto = "Precios: $100, $250, $1500";
const precios = texto.match(/\$\d+/g);
console.log(precios); // ["$100", "$250", "$1500"]
// replace() — reemplazar
const conBlancos = "Hola mundo con espacios";
const normalizado = conBlancos.replace(/\s+/g, " ");
console.log(normalizado); // "Hola mundo con espacios"
// replace con función — transformación dinámica
const resaltado = "hola mundo".replace(
/\b\w+/g,
palabra => palabra.charAt(0).toUpperCase() + palabra.slice(1)
);
console.log(resaltado); // "Hola Mundo"
// split() con regex — separar por patrón
const csv = "uno,dos;tres|cuatro";
const items = csv.split(/[,;|]/);
console.log(items); // ["uno", "dos", "tres", "cuatro"]
// matchAll() — todos los matches con grupos
const htmlLinks = '<a href="url1.com">Link 1</a> <a href="url2.com">Link 2</a>';
const linkRegex = /<a href="([^"]+)">([^<]+)<\/a>/g;
for (const match of htmlLinks.matchAll(linkRegex)) {
console.log(`URL: ${match[1]}, Texto: ${match[2]}`);
}// Validación de email tipada
function validarEmail(email: string): boolean {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
console.log(validarEmail("ana@ejemplo.com")); // true
console.log(validarEmail("no-es-email")); // false
// Extraer todos los números de un string
function extraerNumeros(texto: string): number[] {
return (texto.match(/-?\d+(\.\d+)?/g) ?? []).map(Number);
}
console.log(extraerNumeros("Temp: -5.5°C, máx: 30°C")); // [-5.5, 5, 30]
// Normalizar espacios múltiples
function normalizarEspacios(texto: string): string {
return texto.replace(/\s+/g, " ").trim();
}
console.log(normalizarEspacios(" Hola mundo ")); // "Hola mundo"
// Convertir a Title Case
function titleCase(texto: string): string {
return texto.replace(
/\b\w+/g,
(w: string) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()
);
}
console.log(titleCase("hola MUNDO con TypeScript")); // "Hola Mundo Con Typescript"Salida
true
false
["$100", "$250", "$1500"]
Hola mundo con espacios
Hola Mundo
["uno", "dos", "tres", "cuatro"]
URL: url1.com, Texto: Link 1
URL: url2.com, Texto: Link 2