W3docs

Classes Pattern e Matcher do Java

Compile padrões e encontre correspondências em Java com as classes Pattern e Matcher.

As expressões regulares em Java residem no pacote java.util.regex, e quase todo esse pacote se resume a duas classes: Pattern e Matcher. Um Pattern é uma expressão regular compilada — a regra. Um Matcher é o motor que executa essa regra contra um trecho de entrada e relata o que encontrou. Separar os dois permite compilar uma expressão uma única vez e reutilizá-la em milhares de entradas, o que faz a diferença entre código de regex rápido e código de regex lento.

Este capítulo aborda a compilação de um Pattern, o uso de um Matcher, a crucial distinção entre find() e matches(), grupos de captura e grupos nomeados, e flags. Se você é novo na sintaxe em si, comece pelos capítulos de introdução a regex e sintaxe de regex primeiro.

Pattern: compile uma vez, reutilize para sempre

Pattern.compile(String regex) analisa a sintaxe do regex e retorna um Pattern imutável e thread-safe. A compilação é a etapa custosa, então faça-a uma vez — tipicamente em um campo static final — e compartilhe o resultado. Os métodos de conveniência de String (matches, replaceAll, split) recompilam o padrão a cada chamada, o que é aceitável para uso avulso, mas desperdiçador em um laço.

// Good: compile once, reuse
private static final Pattern EMAIL =
    Pattern.compile("[\\w.+-]+@[\\w.-]+\\.[a-z]{2,}");

// Wasteful in a loop: String.matches recompiles every iteration
for (String s : lines) {
  if (s.matches("[\\w.+-]+@[\\w.-]+\\.[a-z]{2,}")) { /* ... */ }
}

Observe as barras invertidas duplicadas: \\w no código-fonte Java é o token de regex \w, pois a barra invertida precisa primeiro sobreviver ao escape próprio de strings do Java.

Matcher: o motor com estado

Um Pattern não guarda entrada nem posição — é apenas a regra. Chamar pattern.matcher(input) produz um Matcher vinculado àquela entrada, e o Matcher carrega todo o estado mutável: a posição de busca atual, os limites da última correspondência e os grupos capturados. Por ser stateful, um Matcher não é thread-safe; dê a cada thread o seu próprio.

MétodoO que faz
matches()Testa se a entrada inteira corresponde ao padrão
lookingAt()Testa se a entrada corresponde a partir do início (não precisa alcançar o final)
find()Encontra a próxima correspondência em qualquer parte da entrada; retorna true e avança
group() / group(n)Retorna a correspondência inteira, ou o grupo de captura n
start() / end()Índice do primeiro caractere da correspondência e um após o último
replaceAll(repl)Substitui cada correspondência, com referências retroativas $1, $2 para grupos
reset()Rebobina o matcher para a posição zero (opcionalmente com nova entrada)

find() versus matches(): a confusão mais comum

matches() está ancorado à string inteira — retorna true apenas se o padrão consumir toda a entrada. find() é um scanner: procura o padrão em qualquer lugar e pode ser chamado repetidamente para percorrer cada ocorrência.

Pattern p = Pattern.compile("\\d+");
System.out.println(p.matcher("abc123").matches());     // false — whole string isn't digits
System.out.println(p.matcher("abc123").find());        // true  — found "123" inside

Matcher m = p.matcher("a1 b22 c333");
while (m.find()) {
  System.out.println(m.group() + " @ " + m.start());   // 1@1, 22@4, 333@8
}

Um erro frequente é chamar group() antes de um matches()/find() bem-sucedido — isso lança IllegalStateException, pois não há correspondência para ler ainda.

Grupos de captura, grupos nomeados e substituição

Parênteses em um regex criam grupos de captura, numerados da esquerda para a direita começando em 1 (grupo 0 é a correspondência inteira). Java também suporta grupos nomeados com (?<name>...), que você recupera via group("name") — muito mais legível do que contar parênteses. Em strings de substituição, $1 e ${name} inserem o que um grupo capturou. O capítulo de grupos de regex aprofunda grupos não capturantes e referências retroativas.

Pattern date = Pattern.compile("(?<year>\\d{4})-(?<month>\\d{2})-(?<day>\\d{2})");
Matcher m = date.matcher("2024-11-30");
if (m.matches()) {
  System.out.println(m.group("year"));   // 2024
  System.out.println(m.group(3));        // 30  (third numbered group)
}
// Reformat using back-references
System.out.println("2024-11-30".replaceFirst(
    "(\\d{4})-(\\d{2})-(\\d{2})", "$3/$2/$1"));   // 30/11/2024

Flags: insensível a maiúsculas, multilinha e mais

Passe flags como segundo argumento para Pattern.compile, combinadas com |. As mais usadas são CASE_INSENSITIVE, MULTILINE (para que ^/$ correspondam em quebras de linha) e DOTALL (para que . também corresponda a quebras de linha). As mesmas flags podem ser definidas inline com (?i), (?m), (?s). Veja o capítulo de flags de regex para a lista completa e seus prós e contras.

Pattern p = Pattern.compile("error", Pattern.CASE_INSENSITIVE);
System.out.println(p.matcher("FATAL ERROR").find());   // true

// Equivalent inline form:
Pattern.compile("(?i)error");

Um exemplo prático: analisando uma linha de log com um padrão compilado

Este programa compila um padrão de data uma vez e coloca um Matcher à prova — varrendo toda data em uma string com find(), contrastando find() com matches(), reformatando via referências retroativas de grupo, dividindo em espaços em branco e lendo valores de grupos nomeados.

java— editable, runs on the server

O que observar na execução:

  • find() é um scanner com memória. O laço while (m.find()) localizou ambas as datas — no índice 6 e no índice 23 — porque cada chamada retoma de onde a correspondência anterior terminou. É assim que você enumera cada ocorrência, e é por isso que o total resultou em 2.
  • matches() é tudo ou nada. matches whole log? imprimiu false porque as datas estão enterradas em outro texto, enquanto matches one date? imprimiu true porque "2024-01-15" é toda a entrada. Use find() para localizar, matches() para validar.
  • Grupos numerados são acessíveis durante a correspondência. Dentro do laço, m.group(1) retornou apenas o ano de quatro dígitos (2024) enquanto m.group() retornou a data inteira — grupo 0 é a correspondência, grupos 1..n são as capturas entre parênteses da esquerda para a direita.
  • Referências retroativas reorganizam o texto. replaceAll("$3/$2/$1") transformou cada YYYY-MM-DD em DD/MM/YYYY, produzindo start 15/01/2024 build 30/11/2024 done — o motor substituiu cada grupo capturado no template de substituição.
  • Grupos nomeados se leem como campos. Dividir "a b c" em \s+ colapsou as sequências de espaços em 3 partes, e o padrão nomeado permitiu que nm.group("user") e nm.group("host") extraíssem alice e w3docs.com por nome em vez de por posição numérica frágil.

Prática

Prática
Você chama 'pattern.matcher(input)' para obter um Matcher, depois chama imediatamente 'matcher.group()' sem antes chamar 'find()' ou 'matches()'. O que acontece?
Você chama 'pattern.matcher(input)' para obter um Matcher, depois chama imediatamente 'matcher.group()' sem antes chamar 'find()' ou 'matches()'. O que acontece?
Was this page helpful?