W3docs

Grupos e Capturas em Regex no Java

Capture partes do texto correspondido em regex Java com parênteses, grupos numerados e grupos nomeados.

Uma expressão regular não apenas indica se uma string corresponde — ela pode dividir a correspondência em partes que você pode ler. Essas partes são chamadas de grupos. Ao envolver parte de um padrão entre parênteses, você cria um grupo de captura, e quando um Matcher obtém sucesso, você extrai cada grupo pelo número ou pelo nome. Este capítulo aborda grupos numerados, grupos nomeados, retrorreferências, grupos não capturadores e como todos eles se aplicam em substituições.

Grupos de Captura Numerados

Cada par de parênteses em um padrão abre um grupo de captura, numerado da esquerda para a direita pelo ( de abertura. O grupo 0 é especial: ele sempre representa a correspondência completa. Assim, (\d{4})-(\d{2})-(\d{2}) fornece quatro grupos — a data completa mais ano, mês e dia.

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() retorna o número de grupos de captura excluindo o grupo 0, portanto o padrão acima reporta 3. Ler um índice de grupo que não existe lança IndexOutOfBoundsException.

Grupos Nomeados

Contar parênteses se torna frágil à medida que os padrões crescem. Grupos nomeados, escritos como (?<nome>...), permitem ler uma captura por um rótulo legível em vez de um índice. Os nomes devem ser identificadores Java válidos e únicos dentro do padrão.

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
}

Os grupos nomeados ainda são numerados internamente, portanto m.group(1) e m.group("user") retornam o mesmo texto. O nome existe apenas para facilitar a leitura.

Retrorreferências

Uma retrorreferência corresponde ao mesmo texto que um grupo anterior já capturou. Dentro do padrão você escreve \1 para o grupo 1 (ou \k<nome> para um grupo nomeado). É assim que você detecta repetições — por exemplo, uma palavra duplicada — dentro de uma única correspondência.

// \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
}

Observe a barra invertida dupla no código-fonte Java: \\1 na string se torna \1 na regex real. Uma retrorreferência só pode corresponder depois que o seu grupo tiver capturado, portanto o grupo deve aparecer antes no padrão.

Capturas em Substituições

String.replaceAll, Matcher.replaceAll e appendReplacement entendem referências de grupo no texto de substituição. Use $1, $2, ... para grupos numerados e ${nome} para grupos nomeados. Isso transforma a regex em uma pequena ferramenta de reordenação e de templates.

ReferênciaSignificado na substituição
$0A correspondência completa
$1, $2, ...Grupos de captura numerados
${name}Um grupo de captura nomeado
\$Um sinal 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, Ada

Se você precisar de um $ ou \ literal na saída, use o escape \\$ ou \\\\ na string Java.

Grupos Não Capturadores

Às vezes você precisa de parênteses apenas para agrupar uma alternância ou aplicar um quantificador — não para capturar. Um grupo não capturador (?:...) faz exatamente isso: agrupa sem consumir um número de grupo, o que mantém seus índices limpos e deixa o mecanismo ligeiramente mais 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) é não capturador, o host é o grupo 1 e não o grupo 2. Um grupo de captura opcional que não participa da correspondência retorna null, portanto sempre verifique se grupos opcionais são nulos antes de utilizá-los.

Um Exemplo Executável

O programa abaixo exercita todos os tipos de grupos em uma única execução: partes de data numeradas, campos de e-mail nomeados, uma retrorreferência de palavra duplicada, referências de grupo em duas substituições, um grupo de protocolo não capturador e um grupo opcional que retorna null.

java— editable, runs on the server

O que extrair da execução:

  • As duas linhas de data mostram grupos numerados: group(0) é a correspondência completa enquanto group(1..3) são o ano, mês e dia capturados por posição.
  • A linha do e-mail prova que grupos nomeados leem o mesmo texto que os índices, e groupCount=2 conta apenas as capturas nomeadas, nunca o grupo 0.
  • Doubled: the e Doubled: sat vêm da retrorreferência \1 correspondendo ao que o grupo de palavra acabou de capturar — cada palavra repetida é encontrada de forma independente.
  • Swapped: Lovelace, Ada, Turing, Alan mostra $2, $1 reordenando cada par de nomes, enquanto Masked: card [4111] [2222] mostra a referência nomeada ${num} aplicando o template à saída.
  • O (?:https?|ftp) não capturador mantém o host no grupo 1 (w3docs.com), e o grupo de fração opcional imprime frac=null porque nunca participou da correspondência de 42.

Próximos Passos

Os grupos se baseiam no restante do mecanismo de regex, portanto é útil estar confortável com as partes ao redor:

Prática

Prática
No padrão (?:https?|ftp)://(\S+), qual grupo contém a parte do host que (\S+) captura?
No padrão (?:https?|ftp)://(\S+), qual grupo contém a parte do host que (\S+) captura?
Was this page helpful?