Expressões regulares na prática
Expressões regulares têm fama de serem "somente escrita": fáceis de produzir, difíceis de reler e silenciosamente erradas de formas que só aparecem com dados reais. Quase tudo isso vem de alguns poucos mal-entendidos sobre o que o motor faz. Este guia percorre esses pontos e termina com os casos em que o certo é guardar o padrão.
O que o motor está realmente fazendo
Uma expressão regular não descreve a string que você quer — é um pequeno programa que o motor executa em cada posição da entrada, da esquerda para a direita. Ele tenta casar a partir do índice 0; se falhar, vai para o índice 1 e tenta de novo, até algo casar ou a entrada acabar. Quase todo resultado surpreendente decorre dessa varredura.
É por isso que `\d{3}` casa tranquilamente com "abc1234def": encontrou "123" no meio, e nada no padrão dizia o contrário. "Contém três dígitos" e "é exatamente três dígitos" são perguntas diferentes, e o padrão só responde à que você fez.
Âncoras e limites de palavra
`^` e `$` prendem o padrão ao início e ao fim da entrada — ou, com a flag multilinha, ao início e ao fim de cada linha, uma fonte discreta de validadores permissivos demais. `\b` é uma posição de largura zero entre um caractere de palavra e um que não é, então `\bcat\b` encontra o animal em "the cat sat" mas não as letras dentro de "concatenate".
Para validação, ancore o padrão inteiro e teste com os valores que você quer rejeitar, não só com os que quer aceitar. Um validador que deixa passar tudo o que é bom mas também deixa passar lixo é pior que nenhum: empurra a falha para baixo, para um ponto em que rastrear o valor errado é bem mais difícil.
Ganancioso, preguiçoso e retrocesso catastrófico
Quantificadores são gananciosos por padrão: `.*` consome tudo o que consegue e só devolve caracteres quando o resto do padrão falha. Acrescentar `?` os torna preguiçosos. Contra `<b>one</b> and <b>two</b>`, o padrão `<.*>` engole a linha inteira, enquanto `<.*?>` para no primeiro `>` — a mesma intenção, um resultado completamente diferente.
Essa devolução é também onde o desempenho morre. Quantificadores aninhados sobre conjuntos de caracteres sobrepostos — `(a+)+b` é o caso de manual — podem levar o motor a tentar um número exponencial de divisões da entrada antes de admitir que não há correspondência. Com texto fornecido por um atacante isso é uma negação de serviço, chamada ReDoS. Evite que grupos quantificados se sobreponham e prefira uma classe específica a `.` sempre que puder.
Classes de caracteres, o ponto e Unicode
`.` significa "qualquer caractere exceto quebra de linha", a menos que você ligue a flag dotall, e `\d`, `\w` e `\s` são centrados em ASCII na maioria das implementações: `\w` exclui letras acentuadas, e se `\d` inclui dígitos de outras escritas depende do motor. Para texto que não seja ASCII puro, ligue o modo Unicode e use escapes de propriedade como `\p{L}` (qualquer letra) ou `\p{N}` (qualquer número).
Intervalos dentro de uma classe são intervalos de pontos de código, e é por isso que `[A-z]` inclui silenciosamente os seis sinais de pontuação entre `Z` e `a`. Escreva a classe que você quer dizer. E lembre que um texto de aparência idêntica pode ser uma sequência diferente: "é" como um único ponto de código não casa com um padrão feito para "e" seguido de um acento combinante — normalize antes de comparar.
Quando a expressão regular é a ferramenta errada
Formatos aninhados e com aspas — HTML, JSON, CSV, código-fonte — não são linguagens regulares. Um padrão consegue mordiscá-los, mas cada delimitador entre aspas e cada nível de aninhamento acrescenta mais um caso especial, até a expressão ficar ilegível e ainda assim errada na entrada que você ainda não viu. Use um analisador de verdade: para CSV, um que respeite as aspas; para HTML, o DOM.
Endereços de e-mail são a outra armadilha clássica. A gramática da especificação é muito maior que o padrão que as pessoas copiam da internet, então expressões de aparência rigorosa rejeitam rotineiramente endereços válidos — inclusive os bem comuns, com sinal de mais ou um domínio de topo novo e longo. Verifique se há um `@` com algo plausível de cada lado e depois comprove o endereço do único jeito que funciona: enviando um e-mail para ele.
- Case na posição que você quer: ancore com `^`…`$` para validar, deixe solto para buscar.
- Prefira `[^"]*` a `.*?` quando conhece o delimitador — é mais claro e não retrocede.
- Teste com as entradas que espera rejeitar, não apenas com as que espera aceitar.
- Se o formato aninha ou usa aspas, use um analisador.