Introducción a las Expresiones Regulares en Java
Introducción a las expresiones regulares en Java con el paquete java.util.regex.
Una expresión regular (regex) es un patrón compacto que describe un conjunto de cadenas. En Java, el paquete java.util.regex convierte estos patrones en un motor de coincidencia pequeño y rápido que puedes aplicar a cualquier texto — para validar entradas, buscar subcadenas, extraer campos o reescribir contenido. Este capítulo presenta las partes fundamentales antes de que empieces a escribir tus propios patrones.
Qué es una Expresión Regular
Una regex es simplemente una cadena escrita en una sintaxis especial, pero Java no la interpreta carácter por carácter cada vez. En cambio, compilas el patrón una vez en un objeto Pattern y luego lo aplicas a la entrada mediante un Matcher. La forma compilada es una máquina de estados eficiente, por lo que reutilizar un Pattern en muchas entradas es mucho más económico que recompilarlo.
Los patrones describen estructura: un literal como cat coincide exactamente con esas letras, mientras que los metacaracteres describen formas — \d es cualquier dígito, + significa "uno o más", . significa "cualquier carácter". Combínalos y podrás describir números de teléfono, correos electrónicos o líneas de registro en una sola línea de código.
import java.util.regex.Pattern;
public class FirstPattern {
public static void main(String[] args) {
// Compile the pattern once; reuse the result.
Pattern digits = Pattern.compile("\\d+");
System.out.println(digits.matcher("abc123").find()); // true
System.out.println(digits.matcher("hello").find()); // false
}
}Observa la doble barra invertida: \d en regex debe escribirse \\d en un literal de cadena de Java, porque el compilador de Java consume una barra invertida primero.
Pattern y Matcher
Dos clases hacen casi todo el trabajo. Pattern es el plano compilado, reutilizable y seguro para subprocesos. Matcher es el motor con estado que ejecuta ese plano contra una entrada específica — lleva el registro de dónde estás en el texto, qué grupos se capturaron y dónde aterrizó la última coincidencia. Crea un nuevo Matcher por cada entrada; nunca compartas uno entre subprocesos.
| Tipo | Función |
|---|---|
Pattern | El patrón compilado. Inmutable, seguro para subprocesos y reutilizable. |
Matcher | Aplica un Pattern a una entrada. Mantiene el estado de coincidencia. |
Pattern.compile(regex) | Construye un Pattern a partir de una cadena regex. |
pattern.matcher(input) | Devuelve un Matcher vinculado a esa entrada. |
String.matches(regex) | Método de conveniencia que compila y verifica la coincidencia completa en una sola llamada. |
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class PatternAndMatcher {
public static void main(String[] args) {
Pattern p = Pattern.compile("\\w+@\\w+\\.\\w+");
Matcher m = p.matcher("ping me at [email protected] please");
if (m.find()) {
System.out.println("Found email: " + m.group());
}
}
}find() vs matches()
El error más común entre principiantes es confundir las dos formas de ejecutar un patrón. matches() requiere que la entrada completa coincida con el patrón de principio a fin. find() busca cualquier subcadena que coincida y puede llamarse repetidamente para recorrer cada ocurrencia. lookingAt() se sitúa entre ambos: se ancla al inicio pero no requiere que la coincidencia llegue al final.
| Método | ¿Anclado al inicio? | ¿Debe coincidir hasta el final? | ¿Repetible? |
|---|---|---|---|
matches() | sí | sí | no |
lookingAt() | sí | no | no |
find() | no | no | sí |
import java.util.regex.Pattern;
public class FindVsMatches {
public static void main(String[] args) {
Pattern p = Pattern.compile("\\d+");
System.out.println(p.matcher("42").matches()); // true (whole input)
System.out.println(p.matcher("age 42").matches()); // false (extra text)
System.out.println(p.matcher("age 42").find()); // true (substring)
System.out.println(p.matcher("age 42").lookingAt()); // false (no digit at start)
}
}Sintaxis Común con la que Construirás
La mayoría de los patrones reales se ensamblan a partir de un pequeño vocabulario de bloques de construcción: clases de caracteres, atajos predefinidos, cuantificadores y anclas. Aprenderlos te llevará gran parte del camino. Cada construcción tiene su propio capítulo dedicado — consulta Sintaxis de Regex, clases de caracteres y cuantificadores para los detalles completos.
| Construcción | Significado | Ejemplo |
|---|---|---|
. | Cualquier carácter individual (excepto nueva línea) | a.c coincide con abc, axc |
\d \w \s | Dígito, carácter de palabra, espacio en blanco | \d\d coincide con 42 |
[abc] | Cualquiera de a, b, c | [aeiou] coincide con una vocal |
[^abc] | Cualquier carácter excepto a, b, c | [^0-9] coincide con un no dígito |
* + ? | Cero o más, uno o más, cero o uno | ab+ coincide con ab, abb |
{n} {n,m} | Exactamente n, entre n y m | \d{3} coincide con 555 |
^ $ | Inicio, fin de la entrada/línea | ^Hi coincide con un Hi inicial |
(...) | Grupo de captura | (\d{4}) captura cuatro dígitos |
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class GroupsExample {
public static void main(String[] args) {
// Two capturing groups: year and month.
Pattern date = Pattern.compile("(\\d{4})-(\\d{2})");
Matcher m = date.matcher("Released 2025-11 to users");
if (m.find()) {
System.out.println("Full: " + m.group(0)); // 2025-11
System.out.println("Year: " + m.group(1)); // 2025
System.out.println("Month: " + m.group(2)); // 11
}
}
}Ejemplo Práctico
El programa a continuación compila un patrón de número de teléfono y ejercita toda la API sobre él: recorre cada coincidencia con find(), lee los grupos de captura y las posiciones de coincidencia, contrasta find() con matches() y reescribe el texto con replaceAll(). Ejecútalo para ver el motor en acción.
Qué extraer de la ejecución:
find()se llama en un bucle y devuelve dos coincidencias, por lo que el mismoMatcherrecorre el texto una ocurrencia a la vez hasta devolverfalse.group(1)ygroup(2)devuelven las subpartes entre paréntesis (555y1234), mientras quegroup()sin argumento devuelve la coincidencia completa.start()yend()informan los desplazamientos de caracteres de cada coincidencia, que es cómo resaltarías o dividirías el texto original.matches()sobre la oración completa imprimefalseporque el patrón no cubre toda la cadena, mientras que"555-1234"solo imprimetrue— prueba de quematches()es solo para la entrada completa.replaceAll("XXX-XXXX")reescribe cada coincidencia en un solo paso, produciendo la oración enmascarada y mostrando cómo los patrones impulsan la transformación de texto.
Dónde Ir a Continuación
Ahora que conoces las partes en movimiento — Pattern, Matcher y la diferencia entre find() y matches() — profundiza con los capítulos centrados en cada tema:
- Pattern y Matcher — la API completa para compilar y ejecutar patrones.
- Sintaxis de Regex — cada metacarácter y escape, explicado.
- Clases de caracteres — conjuntos, rangos y negación.
- Cuantificadores — repetición greedy, reluctant y possessive.
- Grupos de captura — extracción y backreferencias de subpartes.
- Flags — insensibilidad a mayúsculas, multilínea y otros modificadores.