¿Qué son las Expresiones Regulares? Guía completa

Regex: de jeroglíficos indescifables a súper poderes
Te voy a ser honesto: cuando vi mi primera expresión regular, pensé que alguien había dejado caer café sobre el teclado y había salido algo como /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/. ¿En serio esto hace algo útil?
Pero las expresiones regulares (regex o regexp) son una de esas herramientas que una vez que "clickean" en tu cabeza, se vuelven adictivas. Son básicamente patrones de texto con esteroides que te permiten buscar, validar y manipular cadenas de caracteres de manera súper poderosa.
Es como tener un detective súper inteligente que puede encontrar exactamente lo que buscas en montañas de texto, sin importar qué tan específico o complejo sea el patrón.
¿Por qué me enamoré de las regex?
Los momentos donde brillan:
- Validar emails: En lugar de 50 líneas de código verificando cada parte
- Extraer números de teléfono: De cualquier texto, sin importar el formato
- Limpiar datos sucios: Quitar espacios raros, caracteres molestos
- Buscar y reemplazar como un ninja: En editores, encontrar patrones súper específicos
- Parsing inteligente: Extraer info de logs, archivos, APIs
Mi momento "eureka"
Estaba trabajando en un proyecto donde necesitaba extraer todos los hashtags de literalmente miles de tweets. Mi primer instinto fue escribir un bucle horrible que revisara carácter por carácter, buscara "#", luego letras... uff.
Entonces descubrí: /#\w+/g y BOOM. Una línea. Todos los hashtags. En ese momento entendí que las regex no eran magia negra, sino una herramienta increíblemente práctica.
Lo básico que necesitas dominar
Empecemos súper simple: caracteres literales
// Buscar la palabra "hola" exactamente
const texto = "hola mundo, hola regex";
const patron = /hola/;
console.log(texto.match(patron)); // ["hola"]
El punto mágico (.)
// El punto coincide con cualquier carácter (excepto salto de línea)
const patron = /h.la/;
console.log("hola".match(patron)); // ["hola"]
console.log("hula".match(patron)); // ["hula"]
console.log("h3la".match(patron)); // ["h3la"]
Los corchetes [] (mis favoritos para empezar)
// Coincide con cualquier carácter dentro de los corchetes
const patron = /[aeiou]/; // Cualquier vocal
console.log("programming".match(patron)); // ["o"]
// Rangos (súper útiles)
const numeros = /[0-9]/; // Cualquier dígito
const letras = /[a-z]/; // Cualquier letra minúscula
const todo = /[a-zA-Z0-9]/; // Letras y números
Los corchetes con actitud [^]
// El ^ dentro de [] significa "todo EXCEPTO"
const patron = /[^aeiou]/g; // Todo excepto vocales
console.log("hola".match(patron)); // ["h", "l"]
Los metacaracteres que debes memorizar
Ojo, estos son los que uso constantemente:
Metacarácter | Significado | Ejemplo |
---|---|---|
. | Cualquier carácter | a.b → axb, a5b |
^ | Inicio de línea | ^abc → abc al inicio |
$ | Final de línea | abc$ → abc al final |
\d | Cualquier dígito | \d → 0,1,2...9 |
\w | Carácter de palabra | \w → a-z, A-Z, 0-9, _ |
\s | Espacio en blanco | \s → espacios, tabs |
Te muestro cómo los uso en la vida real:
// \d para dígitos
const telefono = "Mi número es 123-456-7890";
console.log(telefono.match(/\d+/g)); // ["123", "456", "7890"]
// \w para caracteres de palabra
const usuario = "mi_usuario123";
console.log(usuario.match(/\w+/)); // ["mi_usuario123"]
// \s para espacios
const texto = "hola mundo";
console.log(texto.replace(/\s+/g, " ")); // "hola mundo"
Cuantificadores: cuando necesitas especificar "¿cuántos?"
Esta parte al principio me confundía, pero una vez que la entiendes, es súper lógica:
// * = cero o más
const patron1 = /ab*c/; // ac, abc, abbc, abbbc...
console.log("ac".match(patron1)); // ["ac"]
console.log("abbbbc".match(patron1)); // ["abbbbc"]
// + = uno o más
const patron2 = /ab+c/; // abc, abbc, abbbc... (pero NO ac)
console.log("ac".match(patron2)); // null
console.log("abc".match(patron2)); // ["abc"]
// ? = cero o uno (opcional)
const patron3 = /colou?r/; // color o colour
console.log("color".match(patron3)); // ["color"]
console.log("colour".match(patron3)); // ["colour"]
// {n} = exactamente n veces
const patron4 = /\d{3}/; // Exactamente 3 dígitos
console.log("123".match(patron4)); // ["123"]
console.log("12".match(patron4)); // null
// {n,m} = entre n y m veces
const patron5 = /\d{2,4}/; // Entre 2 y 4 dígitos
console.log("12".match(patron5)); // ["12"]
console.log("12345".match(patron5)); // ["1234"]
Grupos: donde la cosa se pone interesante
Los grupos con paréntesis son como "capturar" partes específicas de lo que encuentres:
// Agrupar partes de la expresión
const patron = /(https?):\/\/([^\/]+)/;
const url = "https://www.ejemplo.com/pagina";
const resultado = url.match(patron);
console.log(resultado[0]); // "https://www.ejemplo.com" (coincidencia completa)
console.log(resultado[1]); // "https" (primer grupo)
console.log(resultado[2]); // "www.ejemplo.com" (segundo grupo)
Alternación: "esto O aquello"
// Coincide con una cosa U otra
const patron = /(gato|perro|pájaro)/;
console.log("Tengo un gato".match(patron)); // ["gato"]
console.log("Tengo un perro".match(patron)); // ["perro"]
Casos reales donde las regex me han salvado
1. Validar emails (el clásico)
function validarEmail(email) {
const patron = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
return patron.test(email);
}
console.log(validarEmail("carlos@ejemplo.com")); // true
console.log(validarEmail("email-invalido")); // false
2. Extraer teléfonos de cualquier formato
const texto = `
Contactos:
Juan: (555) 123-4567
María: 555.987.6543
Pedro: 555-111-2222
`;
// Patrón flexible para diferentes formatos
const patronTelefono = /\(?(\d{3})\)?[-.\s]?(\d{3})[-.\s]?(\d{4})/g;
const telefonos = [...texto.matchAll(patronTelefono)];
telefonos.forEach((tel, index) => {
console.log(`Teléfono ${index + 1}: ${tel[1]}-${tel[2]}-${tel[3]}`);
});
3. Limpiar texto sucio (mi favorita)
function limpiarTexto(texto) {
return texto
.replace(/\s+/g, ' ') // Múltiples espacios → un espacio
.replace(/^\s+|\s+$/g, '') // Quitar espacios al inicio/final
.replace(/[^\w\s.-]/g, ''); // Quitar caracteres especiales
}
const textoSucio = " Hola!!! mundo @#$ ";
console.log(limpiarTexto(textoSucio)); // "Hola mundo"
4. Hashtags y mentions (mi momento eureka)
const tweet = "¡Aprendiendo #JavaScript y #regex con @CarlosSeijas! #coding";
// Hashtags
const hashtags = tweet.match(/#\w+/g);
console.log(hashtags); // ["#JavaScript", "#regex", "#coding"]
// Mentions
const mentions = tweet.match(/@\w+/g);
console.log(mentions); // ["@CarlosSeijas"]
5. Passwords seguros
function validarPassword(password) {
// Al menos 8 caracteres, una mayúscula, una minúscula, un número
const patron = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d@$!%*?&]{8,}$/;
return patron.test(password);
}
console.log(validarPassword("Password123")); // true
console.log(validarPassword("password")); // false
Regex en diferentes lenguajes (y mis experiencias)
JavaScript (mi favorito para regex)
// Crear regex
const regex1 = /patrón/flags;
const regex2 = new RegExp('patrón', 'flags');
// Métodos útiles
const texto = "Hola mundo";
texto.match(/mundo/); // Encontrar coincidencia
texto.replace(/mundo/, "regex"); // Reemplazar
texto.search(/mundo/); // Posición de coincidencia
regex1.test(texto); // true/false
Python (más verboso pero poderoso)
import re
# Crear patrones
patron = r'\d+' # r'' para raw strings
texto = "Tengo 25 años"
# Métodos principales
re.search(patron, texto) # Primera coincidencia
re.findall(patron, texto) # Todas las coincidencias
re.sub(patron, 'XX', texto) # Reemplazar
re.match(patron, texto) # Solo al inicio
PHP (funcional)
// Preg functions
$texto = "Hola mundo";
$patron = '/mundo/';
preg_match($patron, $texto); // Primera coincidencia
preg_match_all($patron, $texto); // Todas
preg_replace($patron, 'regex', $texto); // Reemplazar
Herramientas que uso para no volverme loco
Online (mis salvavidas)
- regex101.com - MI FAVORITA. Explica cada pedacito de tu regex
- regexr.com - Súper visual y fácil de usar
- regexpal.com - Para cuando quiero algo rápido y simple
En VS Code (mi truco secreto)
// VS Code: Ctrl+H para buscar/reemplazar con regex
// Activa el botón .* para usar regex
// Ejemplo: Convertir camelCase a snake_case
// Buscar: ([a-z])([A-Z])
// Reemplazar: $1_$2
Técnicas ninja: Lookaheads y Lookbehinds
Esto suena súper fancy, pero en realidad es súper útil:
Positive Lookahead (?=...)
// Encuentra "Java" solo si es seguido por "Script"
const patron = /Java(?=Script)/;
console.log("JavaScript".match(patron)); // ["Java"]
console.log("Java".match(patron)); // null
Negative Lookahead (?!...)
// Encuentra "Java" solo si NO es seguido por "Script"
const patron = /Java(?!Script)/;
console.log("JavaScript".match(patron)); // null
console.log("Java".match(patron)); // ["Java"]
Los errores que me han hecho sufrir (y cómo evitarlos)
1. No escapar caracteres especiales
// ❌ Incorrecto
const patron1 = /3.14/; // El . coincide con cualquier carácter
console.log("3x14".match(patron1)); // ["3x14"] ¡Ups!
// ✅ Correcto
const patron2 = /3\.14/; // Escapar el punto literal
console.log("3.14".match(patron2)); // ["3.14"]
console.log("3x14".match(patron2)); // null
2. Cuantificadores codiciosos (la trampa)
const html = '<div>contenido</div><span>más</span>';
// ❌ Codicioso (toma demasiado)
const patron1 = /<.*>/;
console.log(html.match(patron1)); // ["<div>contenido</div><span>más</span>"]
// ✅ Perezoso (toma lo mínimo)
const patron2 = /<.*?>/;
console.log(html.match(patron2)); // ["<div>"]
3. Olvidar las anclas (me ha pasado mil veces)
// ❌ Sin anclas (encuentra coincidencia parcial)
const patron1 = /\d{3}/;
console.log("12345".match(patron1)); // ["123"] - solo los primeros 3
// ✅ Con anclas (valida toda la cadena)
const patron2 = /^\d{3}$/;
console.log("12345".match(patron2)); // null
console.log("123".match(patron2)); // ["123"]
Casos avanzados que me han impresionado
1. Parser de CSV (magia pura)
function parsearCSV(linea) {
// Maneja comas dentro de comillas
const patron = /,(?=(?:[^"]*"[^"]*")*[^"]*$)/;
return linea.split(patron).map(campo =>
campo.replace(/^"|"$/g, '').replace(/""/g, '"')
);
}
const csv = 'Juan,"Pérez, García",25,"Desarrollador"';
console.log(parsearCSV(csv));
// ["Juan", "Pérez, García", "25", "Desarrollador"]
2. Validar tarjetas de crédito
function validarTarjeta(numero) {
const patrones = {
visa: /^4[0-9]{12}(?:[0-9]{3})?$/,
mastercard: /^5[1-5][0-9]{14}$/,
amex: /^3[47][0-9]{13}$/
};
// Remover espacios y guiones
const numeroLimpio = numero.replace(/[\s-]/g, '');
for (const [tipo, patron] of Object.entries(patrones)) {
if (patron.test(numeroLimpio)) {
return tipo;
}
}
return null;
}
console.log(validarTarjeta("4111 1111 1111 1111")); // "visa"
3. Extraer info de archivos
function extraerInfoArchivo(nombreArchivo) {
const patron = /^(.+?)\.([^.]+)$/;
const match = nombreArchivo.match(patron);
if (match) {
return {
nombre: match[1],
extension: match[2]
};
}
return null;
}
console.log(extraerInfoArchivo("documento.pdf"));
// { nombre: "documento", extension: "pdf" }
Lo que he aprendido después de años usando regex
1. Raw strings son tu amigo
# Python
patron_malo = '\\d+\\s+\\w+' # Muchas barras invertidas
patron_bueno = r'\d+\s+\w+' # Más limpio
2. Comenta las regex complejas (por favor)
const emailRegex =
/^[a-zA-Z0-9._%+-]+ # Usuario: letras, números, algunos símbolos
@ # Símbolo @
[a-zA-Z0-9.-]+ # Dominio: letras, números, puntos, guiones
\. # Punto literal
[a-zA-Z]{2,}$/x # TLD: al menos 2 letras
3. Valida antes de aplicar regex
function buscarPatron(texto, patron) {
// Validar entrada
if (typeof texto !== 'string' || !texto) {
return null;
}
try {
return texto.match(patron);
} catch (error) {
console.error('Error en regex:', error);
return null;
}
}
4. Piensa en el rendimiento
// ❌ Crear regex en cada iteración
function procesarArray(arr) {
return arr.filter(item => /\d+/.test(item));
}
// ✅ Crear regex una vez
const NUMERO_REGEX = /\d+/;
function procesarArray(arr) {
return arr.filter(item => NUMERO_REGEX.test(item));
}
Cuándo NO usar regex (importante)
❌ Para parsear HTML/XML
// ❌ Malo - HTML es demasiado complejo para regex
const patron = /<div.*?>(.*?)<\/div>/;
// ✅ Mejor - usar un parser real
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
❌ Para validaciones súper complejas
// ❌ Esta regex es un monstruo
const fechaCompleja = /^(?:(?:31(\/|-|\.)(?:0?[13578]|1[02]))\1|(?:(?:29|30)(\/|-|\.)(?:0?[1,3-9]|1[0-2])\2))(?:(?:1[6-9]|[2-9]\d)?\d{2})$|^(?:29(\/|-|\.)0?2\3(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:0?[1-9]|1\d|2[0-8])(\/|-|\.)(?:(?:0?[1-9])|(?:1[0-2]))\4(?:(?:1[6-9]|[2-9]\d)?\d{2})$/;
// ✅ Mejor - usar una librería como moment.js o date-fns
const fecha = moment(fechaString, 'DD/MM/YYYY');
Recursos para seguir mejorando
Libros que me ayudaron:
- "Regular Expressions Cookbook" - O'Reilly
- "Mastering Regular Expressions" - Jeffrey Friedl (el biblia)
Para practicar:
- RegexOne - Tutorial interactivo súper bueno
- HackerRank - Retos de regex
- Codewars - Katas específicas de regex
Cheat sheets que uso:
- regexr.com/cheatsheet
- developer.mozilla.org - La documentación de MDN
Mi reflexión final sobre regex
Las regex me cambiaron la forma de pensar sobre los problemas de texto. Al principio, sí, parecían jeroglíficos aliens. Pero una vez que entiendes la lógica, es como aprender un nuevo idioma súper eficiente.
Lo importante es no tratar de aprenderlo todo de una vez. Empieza con patrones súper simples - buscar palabras, validar números básicos. Luego ve agregando complejidad poco a poco.
En mi experiencia, las regex más útiles son las simples. Esas regex gigantes que parecen código Matrix... generalmente hay una forma más simple de hacer las cosas.
Mi consejo práctico: cada vez que te encuentres escribiendo un bucle para procesar texto, pregúntate si una regex podría hacer el trabajo. Muchas veces la respuesta es sí, y te va a ahorrar un montón de código.
No te desanimes si al principio te cuestan - a todos nos pasa. Pero una vez que "clickean", te van a parecer súper lógicas y vas a ver patrones en todas partes.
¿Ya has tenido tu momento eureka con regex? ¿O sigues en la fase de "esto parece chino"? Me encantaría escuchar tu experiencia - las historias de regex siempre son entretenidas, ya sea de éxitos épicos o bugs legendarios.
Comentarios
Posts relacionados

Map vs forEach en JavaScript: Cuándo y Cómo Utilizarlos
Diferencias entre map y forEach en JavaScript, con ejemplos prácticos y casos de uso

Eliminar el Primer Carácter en JavaScript: Guía Rápida
Aprende diferentes métodos para eliminar el primer carácter de un string en JavaScript, como substring() y slice(), con ejemplos prácticos y comparativas.