Grupos y capturas en expresiones regulares de Java
Captura partes del texto en Java con paréntesis, grupos numerados y grupos con nombre en expresiones regulares.
Una expresión regular no solo te dice si una cadena coincide — también puede dividir la coincidencia en partes que puedes leer. Esas partes son los grupos. Al envolver parte de un patrón entre paréntesis creas un grupo de captura y, una vez que un Matcher tiene éxito, puedes extraer cada grupo por número o por nombre. Este capítulo cubre grupos numerados, grupos con nombre, referencias inversas, grupos no capturadores y cómo todos ellos se usan en los reemplazos.
Grupos de captura numerados
Cada par de paréntesis en un patrón abre un grupo de captura, numerados de izquierda a derecha por su ( de apertura. El grupo 0 es especial: siempre representa la coincidencia completa. Así, (\d{4})-(\d{2})-(\d{2}) te da cuatro grupos — la fecha completa más el año, el mes y el día.
Pattern p = Pattern.compile("(\\d{4})-(\\d{2})-(\\d{2})");
Matcher m = p.matcher("2026-05-30");
if (m.matches()) {
System.out.println(m.group(0)); // 2026-05-30 (entire match)
System.out.println(m.group(1)); // 2026
System.out.println(m.group(2)); // 05
System.out.println(m.group(3)); // 30
}groupCount() devuelve el número de grupos de captura excluyendo el grupo 0, por lo que el patrón anterior reporta 3. Leer un índice de grupo que no existe lanza una IndexOutOfBoundsException.
Grupos con nombre
Contar paréntesis se vuelve frágil a medida que los patrones crecen. Los grupos con nombre, escritos como (?<name>...), te permiten leer una captura mediante una etiqueta legible en lugar de un índice. Los nombres deben ser identificadores Java válidos y únicos dentro del patrón.
Pattern p = Pattern.compile("(?<user>[\\w.]+)@(?<host>[\\w.]+)");
Matcher m = p.matcher("[email protected]");
if (m.matches()) {
System.out.println(m.group("user")); // ada
System.out.println(m.group("host")); // math.org
}Los grupos con nombre siguen siendo numerados internamente, por lo que m.group(1) y m.group("user") devuelven el mismo texto. El nombre existe únicamente para mejorar la legibilidad.
Referencias inversas
Una referencia inversa coincide con el mismo texto que un grupo anterior ya capturó. Dentro del patrón se escribe \1 para el grupo 1 (o \k<name> para un grupo con nombre). Así es como se detecta la repetición — por ejemplo, una palabra duplicada — dentro de una sola coincidencia.
// \b(\w+)\s+\1\b matches a word followed by the same word again
Pattern p = Pattern.compile("\\b(\\w+)\\s+\\1\\b");
Matcher m = p.matcher("the the end");
if (m.find()) {
System.out.println(m.group(1)); // the
}Observa la doble barra invertida en el código fuente Java: \\1 en la cadena se convierte en \1 en la expresión regular real. Una referencia inversa solo puede coincidir después de que su grupo haya capturado algo, por lo que el grupo debe aparecer antes en el patrón.
Capturas en reemplazos
String.replaceAll, Matcher.replaceAll y appendReplacement entienden las referencias a grupos en el texto de reemplazo. Usa $1, $2, ... para grupos numerados y ${name} para grupos con nombre. Esto convierte las expresiones regulares en una pequeña herramienta de reordenamiento y plantillas.
| Referencia | Significado en el reemplazo |
|---|---|
$0 | La coincidencia completa |
$1, $2, ... | Grupos de captura numerados |
${name} | Un grupo de captura con nombre |
\$ | Un signo de dólar literal |
// Reorder "First Last" into "Last, First"
String out = "Ada Lovelace".replaceAll("(\\w+)\\s+(\\w+)", "$2, $1");
System.out.println(out); // Lovelace, AdaSi necesitas un $ o \ literal en la salida, escápalo como \\$ o \\\\ en la cadena Java.
Grupos no capturadores
A veces necesitas paréntesis solo para agrupar una alternancia o aplicar un cuantificador — no para capturar. Un grupo no capturador (?:...) hace exactamente eso: agrupa sin consumir un número de grupo, lo que mantiene tus índices limpios y el motor ligeramente más rápido.
// Group the protocol alternation, but capture only the host
Pattern p = Pattern.compile("(?:https?|ftp)://(\\S+)");
Matcher m = p.matcher("https://w3docs.com");
if (m.find()) {
System.out.println(m.group(1)); // w3docs.com (group 1, not 2)
}Como (?:https?|ftp) no es capturador, el host es el grupo 1 en lugar del grupo 2. Un grupo de captura opcional que no participa en la coincidencia devuelve null, así que siempre comprueba si es null antes de usar grupos opcionales.
Un ejemplo ejecutable
El programa siguiente ejercita todos los tipos de grupos en una sola ejecución: partes de fecha numeradas, campos de correo con nombre, una referencia inversa a palabra duplicada, referencias a grupos en dos reemplazos, un grupo de protocolo no capturador y un grupo opcional que devuelve null.
Lo que se obtiene de la ejecución:
- Las dos líneas de fecha muestran grupos numerados:
group(0)es la coincidencia completa, mientras quegroup(1..3)son año, mes y día capturados por posición. - La línea del correo electrónico demuestra que los grupos con nombre leen el mismo texto que los índices, y
groupCount=2cuenta solo las capturas con nombre, nunca el grupo 0. Doubled: theyDoubled: satprovienen de la referencia inversa\1que coincide con lo que el grupo de palabras acaba de capturar — cada palabra repetida se encuentra de forma independiente.Swapped: Lovelace, Ada, Turing, Alanmuestra$2, $1reordenando cada par de nombres, mientras queMasked: card [4111] [2222]muestra la referencia con nombre${num}como plantilla de la salida.- El
(?:https?|ftp)no capturador mantiene el host en el grupo 1 (w3docs.com), y el grupo de fracción opcional imprimefrac=nullporque nunca participó en la coincidencia con42.
Qué ver a continuación
Los grupos se apoyan en el resto del motor de expresiones regulares, por lo que conviene dominar las piezas que los rodean:
- Pattern y Matcher — las clases cuyos métodos
group(),groupCount()yreplaceAll()se llaman aquí. - Cuantificadores de regex —
{4},+y?determinan cuánto captura cada grupo. - Clases de caracteres de regex —
\d,\wy\Sson los bloques de construcción más comunes de los grupos. - Flags de regex — flags como
CASE_INSENSITIVEcambian lo que coinciden tus grupos.