W3docs

Quantificadores de Regex em Java

Como os quantificadores de regex Java (*, +, ?, {n,m}) se comportam nos modos greedy, relutante e possessivo.

Um quantificador define quantas vezes o elemento anterior pode se repetir. * significa "zero ou mais", + significa "um ou mais", ? significa "zero ou um", e {n,m} define um intervalo exato. Isso é comum a todos os flavors de regex. O que confunde as pessoas em Java é que cada quantificador existe em três modosgreedy, relutante e possessivo — e o modo, não a contagem, determina como o motor de regex em java.util.regex percorre a entrada.

Esta página aborda os quatro quantificadores básicos, os três modos de correspondência, por que padrões greedy fazem correspondências excessivas e como os quantificadores possessivos protegem contra backtracking catastrófico. Assume-se que você já sabe como compilar um Pattern e executar um Matcher; caso contrário, comece com Pattern e Matcher. Para os metacaracteres que você irá repetir, veja classes de caracteres, e para capturar o texto repetido, veja grupos.

Os quatro quantificadores básicos

Anexe um quantificador a um único caractere, uma classe de caracteres ou um grupo, e ele controla a repetição do elemento imediatamente à sua esquerda:

QuantificadorRepete o elemento anteriorExemploCorresponde a
*zero ou mais vezesab*cac, abc, abbbc
+uma ou mais vezesab+cabc, abbc (não ac)
?zero ou uma vezcolou?rcolor, colour
{n}exatamente n vezes\d{4}um ano com 4 dígitos
{n,}n ou mais vezes\d{2,}dois ou mais dígitos
{n,m}entre n e m vezes\d{3,5}3 a 5 dígitos
Pattern.matches("ab*c", "abbbc");   // true  — three b's
Pattern.matches("ab+c", "ac");      // false — '+' needs at least one b
Pattern.matches("colou?r", "color");// true  — the 'u' is optional
Pattern.matches("\\d{3,5}", "1234");// true  — four digits is within 3..5

Greedy é o padrão

Um quantificador simples é greedy: ele consome o máximo possível da entrada e depois faz backtracking — devolvendo caracteres um a um — até que o restante do padrão possa corresponder. É por isso que um <.+> ingênuo contra HTML consome muito mais do que uma tag:

String html = "<b>one</b>";
Matcher m = Pattern.compile("<.+>").matcher(html);
m.find();
m.group(); // "<b>one</b>"  — '.+' ate everything, then backed up to the last '>'

O motor primeiro capturou a string inteira, não encontrou > no final e caminhou para trás até encontrar um > — parando no último deles.

Relutante: adicione ? para capturar o mínimo possível

Acrescente ? a qualquer quantificador (*?, +?, ??, {n,m}?) e ele se torna relutante (também chamado de lazy): ele corresponde ao menor número de repetições primeiro e expande apenas quando forçado. É o que você geralmente quer ao varrer tokens delimitados:

String html = "<b>one</b>";
Matcher m = Pattern.compile("<.+?>").matcher(html);
m.find();
m.group(); // "<b>"  — stopped at the first '>'

Mesmo padrão, um caractere a mais, comportamento oposto: o greedy <.+> retorna a string inteira enquanto o relutante <.+?> retorna apenas a primeira tag.

Possessivo: adicione + e nunca devolva

Acrescente + (*+, ++, ?+, {n,m}+) e o quantificador se torna possessivo: ele captura o máximo possível como um greedy, mas recusa-se a fazer backtracking. Se o restante do padrão falhar, a correspondência inteira falha — não há retrocesso para resgatá-la.

// Possessive '.++' eats the final '>' too and won't return it, so no '>' is left
Pattern.compile("<.++>").matcher("<b>one</b>").find(); // false

Por que abrir mão dessa flexibilidade? Velocidade e segurança. Como um quantificador possessivo nunca reconsidera, ele não pode cair em backtracking catastrófico — o colapso exponencial que congela uma thread em padrões como (a+)+b alimentado com uma longa sequência de a sem b. O possessivo a++b responde "sem correspondência" quase instantaneamente.

ModoSintaxeEstratégiaFaz backtracking?
GreedyX*captura o máximo, depois recua conforme necessáriosim
RelutanteX*?captura o mínimo, depois expande conforme necessáriosim
PossessivoX*+captura o máximo e mantémnão

Um exemplo prático: os três modos lado a lado

Este programa executa a mesma entrada com quantificadores greedy, relutante e possessivo, depois exercita o intervalo {n,m} e um quantificador de grupo. Tudo aqui é java.util.regex puro do JDK.

java— editable, runs on the server

O que extrair da execução:

  • O greedy <.+> exibiu <b>one</b><i>two</i> — a string inteira. Ele consumiu tudo e depois recuou até o último >, o que explica exatamente por que padrões greedy fazem correspondências excessivas entre delimitadores.
  • O relutante <.+?> exibiu <b> com a mesma entrada. O simples ? inverteu a estratégia de "máximo" para "mínimo", parando no primeiro > — a correção para varrer tag por tag.
  • O possessivo <.++> exibiu matches=false. Ele engoliu o > final e recusou-se a devolvê-lo, então o > no final do padrão não tinha com o que corresponder e toda a tentativa falhou — o preço de nunca fazer backtracking.
  • \d{3,5} rejeitou 12 (no match, dígitos insuficientes), aceitou 123 e 12345 inteiros, e em 1234567 correspondeu apenas a 12345 (len 5) — o limite superior 5 o restringiu mesmo com mais dígitos disponíveis.
  • O padrão de grupo (\w+\s*){2,3} correspondeu a alpha beta gamma — três palavras, seu máximo — comprovando que o quantificador se aplicou ao grupo entre parênteses inteiro, e a++b retornou false instantaneamente em uma longa sequência de a sem b, mostrando como os quantificadores possessivos evitam o backtracking catastrófico.

Qual modo devo usar?

  • Prefira o relutante (*?, +?) ao corresponder conteúdo entre delimitadores — aspas, tags, colchetes, blocos delimitados. Ele para no primeiro delimitador de fechamento em vez do último, que é quase sempre o que você quer dizer.
  • Mantenha o greedy (o padrão) quando você genuinamente deseja a correspondência mais longa possível, ou quando há apenas uma correspondência possível de qualquer forma e o ?/+ extra seria apenas ruído.
  • Use o possessivo (*+, ++) como uma ferramenta de desempenho e segurança em entradas que você não controla. Por nunca fazer backtracking, ele não pode desencadear backtracking catastrófico, mas também falhará em correspondências que um quantificador greedy teria resgatado — portanto, aplique-o apenas onde você sabe que o backtracking é desnecessário.

Uma correção comum no mundo real: um regex que funciona em entradas pequenas mas trava em entradas grandes geralmente tem um quantificador greedy dentro de um grupo, como (\w+)+. Tornar o quantificador interno possessivo ((\w++)+ ou reestruturando o padrão) elimina o colapso exponencial.

Prática

Prática
Contra a entrada '<b>one</b>', o que o padrão Java '<.+?>' (um quantificador relutante) corresponde na primeira chamada a find()?
Contra a entrada '<b>one</b>', o que o padrão Java '<.+?>' (um quantificador relutante) corresponde na primeira chamada a find()?
Was this page helpful?