> ## Documentation Index
> Fetch the complete documentation index at: https://apuntes.simulab.es/llms.txt
> Use this file to discover all available pages before exploring further.

# Expresiones regulares (regex): sintaxis, grupos y ejemplos en Python

> Expresiones regulares explicadas: caracteres literales y especiales, clases de caracteres, cuantificadores, anclas, grupos y capturas, alternativas, cuantificadores voraces y perezosos, el módulo re de Python (search, findall, sub, split) y ejemplos para validar correos, teléfonos, fechas y DNI.

¿Cómo encontrar todos los correos electrónicos de un texto, comprobar que un DNI tiene el formato correcto o cambiar todas las fechas de `12/10/2026` a `2026-10-12`? Las **expresiones regulares** (*regex*) describen **patrones de texto** con una notación compacta, y casi todos los lenguajes y editores las entienden. Al principio parecen jeroglíficos, pero con una docena de símbolos se resuelve la mayoría de los casos.

## Lo que vas a aprender

* Los caracteres especiales y las clases de caracteres.
* Los cuantificadores y las anclas.
* Los grupos, las capturas y las alternativas.
* La diferencia entre cuantificadores voraces y perezosos.
* Cómo usarlas en Python con el módulo `re`.
* Patrones útiles para validar datos.

## Fundamentos

### Caracteres

La mayoría de los caracteres se representan a sí mismos: el patrón `gato` encuentra "gato". Algunos son **especiales**: `. ^ $ * + ? { } [ ] \ | ( )`. Para buscarlos literalmente se escapan con `\`: `\.` es un punto.

### Clases de caracteres

| Patrón | Coincide con |
| - | - |
| `.` | Cualquier carácter salvo el salto de línea |
| `[abc]` | Una a, una b o una c |
| `[a-z]` | Una letra minúscula (sin ñ ni tildes) |
| `[^0-9]` | Cualquier cosa que **no** sea un dígito |
| `\d` | Un dígito (como `[0-9]`) |
| `\w` | Un carácter de palabra: letra, dígito o `_` |
| `\s` | Un espacio en blanco (espacio, tabulador, salto de línea) |
| `\D`, `\W`, `\S` | Lo contrario de las anteriores |

En Python, `\w` incluye por defecto letras con tilde y la ñ.

### Cuantificadores

| Patrón | Repeticiones |
| - | - |
| `*` | 0 o más |
| `+` | 1 o más |
| `?` | 0 o 1 (opcional) |
| `{3}` | Exactamente 3 |
| `{2,4}` | De 2 a 4 |
| `{2,}` | 2 o más |

Se aplican a lo que tienen justo delante: `ab+` es "a" seguida de una o más "b"; `(ab)+` es "ab" repetido.

### Anclas

| Patrón | Posición |
| - | - |
| `^` | Inicio del texto (o de la línea con `re.MULTILINE`) |
| `$` | Final del texto (o de la línea) |
| `\b` | Límite de palabra |

Para **validar** que todo el texto cumple un formato se usa `^…$` (o `re.fullmatch`). Sin anclas, el patrón se busca en cualquier parte.

### Grupos y alternativas

* `( … )` agrupa y **captura** lo que coincide, para usarlo después.
* `(?: … )` agrupa sin capturar.
* `(?P<nombre> … )` captura con nombre.
* `a|b` es una alternativa: "a" o "b".
* `\1`, `\2` se refieren a lo capturado por el grupo 1, 2…

### Voraces y perezosos

Por defecto los cuantificadores son **voraces**: cogen todo lo posible. En `<b>uno</b> y <b>dos</b>`, el patrón `<b>.*</b>` coincide con **todo**, de la primera `<b>` a la última `</b>`. Añadiendo `?` se vuelven **perezosos** (lo mínimo posible): `<b>.*?</b>` coincide con cada etiqueta por separado.

### El módulo re de Python

Se escriben los patrones como **cadenas crudas** `r"…"`, para que Python no interprete las barras invertidas.

| Función | Hace |
| - | - |
| `re.search(p, s)` | Busca la primera coincidencia en cualquier parte; devuelve un objeto `Match` o `None` |
| `re.fullmatch(p, s)` | Comprueba que **todo** el texto coincide |
| `re.findall(p, s)` | Lista con todas las coincidencias (o con los grupos, si hay) |
| `re.finditer(p, s)` | Iterador de objetos `Match` |
| `re.sub(p, r, s)` | Sustituye las coincidencias |
| `re.split(p, s)` | Divide el texto por el patrón |
| `re.compile(p)` | Precompila un patrón que se va a usar mucho |

Opciones útiles: `re.IGNORECASE` (sin distinguir mayúsculas) y `re.MULTILINE`.

## Ejemplos resueltos

> [accordion] Ejemplo 1 · Extraer números
>
> ```python theme={null}
> import re
> texto = "Compré 3 libros por 45,90 € y 2 cuadernos por 3,50 €"
> print(re.findall(r"\d+(?:,\d+)?", texto))
> # ['3', '45,90', '2', '3,50']
> ```

> [accordion] Ejemplo 2 · Validar un DNI
>
> Ocho dígitos y una letra mayúscula:
>
> ```python theme={null}
> def formato_dni(s):
>     return re.fullmatch(r"\d{8}[A-Z]", s) is not None
>
> print(formato_dni("12345678Z"), formato_dni("1234567Z"))   # True False
> ```
>
> La regex solo comprueba el **formato**. La letra correcta se calcula aparte: es la posición `número % 23` en la cadena `"TRWAGMYFPDXBNJZSQVHLCKE"`.

> [accordion] Ejemplo 3 · Buscar correos electrónicos
>
> ```python theme={null}
> texto = "Escribe a ana.lopez@uni.es o a soporte@simulab.es"
> print(re.findall(r"[\w.+-]+@[\w-]+\.[\w.]+", texto))
> # ['ana.lopez@uni.es', 'soporte@simulab.es']
> ```
>
> Un patrón que acepte **exactamente** los correos válidos según el estándar es enorme; para validar en serio, lo práctico es una comprobación sencilla y enviar un correo de confirmación.

> [accordion] Ejemplo 4 · Cambiar el formato de las fechas
>
> ```python theme={null}
> texto = "Examen el 12/10/2026 y entrega el 03/11/2026"
> print(re.sub(r"(\d{2})/(\d{2})/(\d{4})", r"\3-\2-\1", texto))
> # Examen el 2026-10-12 y entrega el 2026-11-03
> ```
>
> Los grupos capturan día, mes y año, y en la sustitución se reordenan.

> [accordion] Ejemplo 5 · Grupos con nombre
>
> ```python theme={null}
> m = re.search(r"(?P<hora>\d{1,2}):(?P<min>\d{2})", "La clase empieza a las 9:30")
> print(m.group("hora"), m.group("min"))   # 9 30
> ```

> [accordion] Ejemplo 6 · Voraz frente a perezoso
>
> ```python theme={null}
> html = "<b>uno</b> y <b>dos</b>"
> print(re.findall(r"<b>(.*)</b>", html))    # ['uno</b> y <b>dos']
> print(re.findall(r"<b>(.*?)</b>", html))   # ['uno', 'dos']
> ```

> [accordion] Ejemplo 7 · Teléfonos españoles
>
> Nueve dígitos que empiezan por 6, 7, 8 o 9, con espacios opcionales cada tres cifras y prefijo +34 opcional:
>
> ```python theme={null}
> patron = r"(?:\+34\s?)?[6-9]\d{2}\s?\d{3}\s?\d{3}"
> print(re.findall(patron, "Llama al 612 345 678 o al +34 912345678"))
> # ['612 345 678', '+34 912345678']
> ```

## Errores frecuentes

* **Olvidar escapar el punto.** `.` es "cualquier carácter"; para un punto literal, `\.`.
* **Validar sin anclas.** `re.search(r"\d{8}[A-Z]", "abc12345678Zxyz")` encuentra coincidencia; usa `fullmatch` o `^…$`.
* **No usar cadenas crudas** `r"…"` en Python: `"\b"` es un retroceso, no un límite de palabra.
* **Usar `.*` voraz** cuando hay varias coincidencias en la misma línea.
* **Intentar procesar HTML o JSON con regex.** Para eso hay analizadores específicos; las regex fallan con estructuras anidadas.

## Preguntas frecuentes

> [accordion] ¿Qué relación tienen con los autómatas?
>
> Las expresiones regulares (en su forma teórica) describen exactamente los lenguajes que reconoce un **autómata finito**, como las [máquinas de estados](https://apuntes.simulab.es/electronica/logica-secuencial/maquinas-estados.md). Los motores de regex convierten el patrón en un autómata y lo recorren con el texto. Las regex de los lenguajes modernos añaden extras (referencias hacia atrás) que ya no son "regulares" en sentido estricto.

> [accordion] ¿Dónde más se usan?
>
> En los editores de código (buscar y reemplazar), en la terminal (`grep`, `sed`), en hojas de cálculo, en bases de datos (`REGEXP` en [SQL](https://apuntes.simulab.es/programacion/bases-datos/sql.md)), en la validación de formularios web y en el análisis de registros (logs) de servidores.

## Temas relacionados

> [card] [Fundamentos de Python](https://apuntes.simulab.es/programacion/python/fundamentos-python.md)
>
> Cadenas y métodos de texto.

> [card] [Máquinas de estados](https://apuntes.simulab.es/electronica/logica-secuencial/maquinas-estados.md)
>
> Los autómatas detrás de las regex.

> [card] [Git](https://apuntes.simulab.es/programacion/herramientas/git.md)
>
> Otra herramienta del día a día.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.