Expressões regulares

Classes, âncoras, quantificadores, alternância, capturas, referências, lookaround e backtracking.

TLDR

Perguntar sobre esta página

ChatGPTClaudePerplexityGrokGeminiCopiar e abrirDeepSeekCopiar e abrir

Encontrar LTW-042 numa frase e validar um campo que deve conter apenas esse código são tarefas diferentes. Na pesquisa, texto antes ou depois pode ser permitido. Na validação, xLTW-042 tem de falhar, mesmo contendo um código reconhecível.

Peças e precedência

Em LTW-042, precisamos de reconhecer o prefixo, o hífen e os dígitos. [0-9] aceita um dígito, e [0-9]{3} exige três. As âncoras limitam a posição em que o padrão pode casar. A tabela reúne essas peças e as outras operações usadas abaixo.

PadrãoSignificado habitual
abcOs caracteres literais nessa ordem
[abc], [0-9]Um caráter do conjunto ou intervalo
[^0-9]Um caráter fora do conjunto
.Um caráter, habitualmente excluindo mudanças de linha
\d, \w, \sClasses abreviadas de dígito, palavra e espaço
^, $Posições de início e fim, com pormenores dependentes do motor e flags
\bFronteira de palavra, sem consumir caracteres
a?, a*, a+Zero ou um, zero ou mais, um ou mais a
a{3}, a{2,4}Exatamente três, ou entre dois e quatro a
a|bAlternativa, quando a barra não está escapada no padrão
(ab)Grupo que captura
(?:ab)Grupo sem captura

As abreviaturas e o tratamento de Unicode dependem do motor; [A-Za-z] não representa todas as letras portuguesas. Quantificadores aplicam-se à peça anterior: ab+ exige um a e um ou mais b; (ab)+ repete o par.

Alternância tem baixa precedência. Em ^LTW|BD$, as âncoras prendem-se a cada alternativa separadamente: o padrão aceita entradas que comecem por LTW ou terminem por BD. Para prender ambas as alternativas às pontas, agrupa primeiro: ^(?:LTW|BD)$.

O vídeo mostra classes, grupos e quantificadores. Pausa cada exemplo para prever a correspondência antes de a veres.

Abrir no YouTube

Construir e testar um formato

Queremos aceitar LTW-042 ou BD-007, com três dígitos no fim, e capturar esses dígitos. As peças são início, grupo de prefixos, hífen, grupo de três dígitos e fim:

/^(?:LTW|BD)-([0-9]{3})$/;

O primeiro grupo só organiza alternativas. O segundo captura e, por ser o primeiro grupo capturante, fica em índice 1. O índice 0 é sempre a correspondência completa.

Testar códigos e capturas
const codigo = /^(?:LTW|BD)-([0-9]{3})$/;
for (const texto of ['LTW-042', 'BD-007', 'LTW-42', 'xLTW-042']) {
  const resultado = texto.match(codigo);
  console.log(texto + ': ' + (resultado ? resultado[1] : 'inválido'));
}
const frase = 'A <b>rede</b> e <i>web</i>.';
console.log(frase.match(/<.*>/)[0]);
console.log(frase.match(/<.*?>/)[0]);
console.log('livro livro novo'.match(/\b(\w+)\s+\1\b/)[0]);
Resultado

Executa o código para ver a saída e os gráficos.

A primeira parte escreve:

LTW-042: 042
BD-007: 007
LTW-42: inválido
xLTW-042: inválido

Dois dígitos falham a repetição e o x inicial falha a âncora. Isto verifica o formato, não prova que o código exista no catálogo. Para dados externos, define também limites de comprimento e regras semânticas.

Guloso, preguiçoso e primeiro resultado

No texto A <b>rede</b> e <i>web</i>., <.*> escolhe <b>rede</b> e <i>web</i>. O * guloso tenta consumir tanto quanto possível e depois recua até conseguir o > final. É por isso que a correspondência atravessa a primeira etiqueta de fecho.

<.*?> escolhe primeiro <b>: o quantificador preguiçoso tenta o mínimo e expande se o restante padrão não puder casar. <[^>]*> também pára antes do primeiro >, porque a classe negada nunca consome esse caráter. Estes padrões servem para observar quantificadores, mas não são parsers seguros de HTML: aspas, comentários e estrutura aninhada exigem um parser.

Ao pedir a primeira correspondência, o motor começa na primeira posição em que o padrão pode casar. “Guloso” não significa escolher uma posição inicial mais à direita para obter um resultado maior. Flags como g em JavaScript pedem correspondências globais; i ignora diferenças de maiúsculas conforme as regras do motor; m altera as âncoras para linhas; s permite que . inclua quebras de linha.

Capturas e referências

Uma referência \1 exige o mesmo texto que o primeiro grupo capturou:

/\b(\w+)\s+\1\b/.exec('livro livro novo')[0]; // livro livro

Não exige “outra palavra qualquer”; exige livro de novo. Numa substituição JavaScript, $1 refere a primeira captura:

'2026-10-02'.replace(/^(\d{4})-(\d{2})-(\d{2})$/, '$3/$2/$1'); // 02/10/2026

O padrão verifica a forma numérica, não que o dia exista no calendário. 2026-02-31 também tem a forma certa. Valida a data com regras de calendário quando esse é o objetivo.

Lookaround e backtracking

Lookaround verifica contexto sem o incluir na correspondência. livro(?=s) escolhe livro apenas quando vem seguido de s; livro(?!s) exige que não venha s. (?<=EUR )[0-9]+ encontra dígitos precedidos de EUR ; o prefixo não faz parte do resultado. Lookbehind e algumas regras têm limites de compatibilidade entre motores.

Backtracking é a tentativa de alternativas após uma escolha não permitir acabar o padrão. Em a.*b, o motor pode consumir até ao fim e recuar à procura de um b final. Padrões com repetições ambíguas, como (a+)+$, podem exigir muitas tentativas numa entrada quase válida. Limita a entrada e evita estruturas com custo explosivo em dados controlados pelo cliente.

Expressões regulares clássicas correspondem a linguagens reconhecíveis por autómatos finitos. Motores práticos incluem extensões, como referências a capturas, que ultrapassam esse modelo. Não uses a palavra “regular” para assumir que qualquer padrão de um motor moderno tem custo simples ou expressividade idêntica.

JavaScript, PHP e HTML

Em JavaScript, test devolve booleano e exec/match permitem obter capturas. match sem g devolve a primeira correspondência e as capturas; com g, devolve as correspondências completas, sem as capturas de cada uma. Para obter capturas de todas, usa matchAll com g.

const texto = 'LTW-042 e BD-007';
for (const resultado of texto.matchAll(/(?:LTW|BD)-([0-9]{3})/g)) {
  console.log(resultado[1]); // 042, depois 007
}

Um objeto regex com flag g conserva lastIndex entre chamadas de test: /a/g sobre 'a' dá true na primeira chamada e false na segunda com o mesmo objeto. Evita g numa validação repetida sem controlar esse estado.

Em PHP, os padrões PCRE precisam de delimitadores. preg_match devolve 1 para correspondência, 0 para ausência e false para erro. Para extrair todas as ocorrências usa preg_match_all, que preenche um array com a correspondência completa e as capturas. Para substituir usa preg_replace, com $1 para a primeira captura no texto de substituição. Para exigir o texto completo de forma estrita em PCRE, \A e \z fixam as pontas sem o caso especial de uma quebra de linha final de $:

<?php
$valido = preg_match('/\A(?:LTW|BD)-[0-9]{3}\z/', $codigo) === 1;

No HTML, pattern recebe o padrão sem /.../ e aplica-o ao valor completo nos tipos suportados. pattern="(?:LTW|BD)-[0-9]{3}" não exige sozinho uma entrada não vazia; acrescenta required se for obrigatória. A sintaxe atual usa regras Unicode próprias, por isso confirma compatibilidade ao usar classes avançadas. O servidor tem de validar novamente, independentemente do formulário.

Exercícios

Prever a primeira correspondência

No texto preço [12] e [35], escreve a primeira correspondência de /\[.*\]/ e /\[.*?\]/. Propõe uma classe negada que pare no primeiro fecho.

Pista

O início é o primeiro [ onde o padrão pode casar.

Mais uma pista

Guloso tenta o máximo; preguiçoso tenta o mínimo que permite acabar.

Solução

O guloso produz uma única correspondência, [12] e [35], do primeiro [ ao último ]. O preguiçoso produz [12]. Uma alternativa é /\[[^\]]*\]/, que admite apenas caracteres diferentes de ] dentro dos colchetes. A escolha gulosa não começa automaticamente no segundo [ para obter outra correspondência.

Confere a tua resposta:

  • Obtive os dois resultados distintos.
  • Defini a classe que exclui o fecho.

Erros frequentes

Não esquecer que * admite zero caracteres nem confundir primeiro início com menor resultado.

Voltar à explicação
Exigir a mesma palavra

Compara /\b(\w+)\s+\1\b/ e /\b\w+\s+\w+\b/ sobre rede web rede rede. Qual o primeiro resultado de cada?

Pista

A referência exige o texto da primeira captura.

Solução

O padrão com referência tenta primeiro em cada posição inicial. Em rede web, a captura é rede mas o texto seguinte é web, por isso falha. Em web rede, a captura é web e o seguinte é rede, por isso falha. Em rede rede no fim, a repetição confirma-se. O segundo padrão só exige duas palavras e escolhe rede web logo no início. A correspondência global não foi pedida, por isso paramos no primeiro resultado de cada padrão.

Confere a tua resposta:

  • Distingui identidade de formato de palavra.
  • Indiquei o primeiro resultado de cada.

Erros frequentes

Não tratar \1 como um novo grupo livre nem escolher a ocorrência repetida final em ambos.

Voltar à explicação
Validar formato e significado

Uma aplicação extrai datas com /\b\d{4}-\d{2}-\d{2}\b/ de Entrega: 2026-02-31; próxima: 2026-03-01. Usa depois (?!;) para recusar uma correspondência seguida imediatamente de ponto e vírgula. Escreve o novo padrão, prevê o resultado de cada versão e explica que validação ainda falta antes de aceitar uma data.

Pista

Segue primeiro a correspondência completa; só depois verifica o caráter seguinte.

Mais uma pista

Coloca (?!;) depois da fronteira final. O mês e o dia ainda são apenas pares de dígitos.

Solução

O padrão original encontra primeiro 2026-02-31. Com /\b\d{4}-\d{2}-\d{2}\b(?!;)/, essa ocorrência falha porque vem seguida de ;; a primeira correspondência passa a ser 2026-03-01. O lookahead não consome caracteres. A fronteira final impede aceitar apenas um prefixo dos dígitos para fugir ao teste do separador. Nenhuma versão valida o calendário: fevereiro não tem 31 dias. Depois de extrair, verifica mês, dia e ano, incluindo anos bissextos.

Confere a tua resposta:

  • Usei uma asserção que não consome o separador e obtive 2026-03-01.
  • Distingui extração, contexto e validade da data.

Erros frequentes

Não substituir a asserção por uma classe que consome o separador, nem aceitar uma data só porque tem o formato certo.

Voltar à explicação
Devolver a captura ou ausência

Define numeroCodigo(texto): recebe uma string sem quebras de linha. Se toda a string tiver prefixo LTW ou BD, hífen e exatamente três dígitos de 0 a 9, devolve os dígitos como string. Caso contrário, devolve null. Chamadas repetidas devem dar o mesmo resultado.

JavaScript
function numeroCodigo(texto) {
  return null;
}
Testes e resultado

Verifica o código para comparar o resultado com os testes.

Testes
  1. Zeros e ambos os prefixos
    Código do teste
    console.log(JSON.stringify(numeroCodigo('LTW-042')));
    console.log(JSON.stringify(numeroCodigo('BD-007')));
    Esperado:
    "042"
    "007"
  2. Comprimento e prefixo inválidos
    Código do teste
    console.log(numeroCodigo('LTW-42'));
    console.log(numeroCodigo('LTW-0042'));
    console.log(numeroCodigo('IA-042'));
    console.log(numeroCodigo('ltw-042'));
    Esperado:
    null
    null
    null
    null
  3. Entrada completa
    Código do teste
    console.log(numeroCodigo('xLTW-042'));
    console.log(numeroCodigo('LTW-042x'));
    console.log(numeroCodigo(''));
    Esperado:
    null
    null
    null
  4. Validação repetida
    Código do teste
    console.log(JSON.stringify(numeroCodigo('BD-000')));
    console.log(JSON.stringify(numeroCodigo('BD-000')));
    Esperado:
    "000"
    "000"

Pista
Usa um grupo que capture os dígitos e fixa ambas as pontas do padrão.
Mais uma pista
match devolve null quando não há correspondência. O índice 1 contém a primeira captura.
Solução
function numeroCodigo(texto) {
  const resultado = texto.match(/^(?:LTW|BD)-([0-9]{3})$/);
  return resultado === null ? null : resultado[1];
}

exec sem flag global também resolve. Uma validação manual que cumpra o mesmo contrato passa os testes. Preserva os zeros, porque a captura é texto.

Erros frequentes

Não converter para número, devolver a correspondência inteira, aceitar apenas parte da entrada nem confundir null com a string 'null'.
Voltar à explicação

À tua maneira

Aparência

Modo de cor
Tema
Cor de destaque

Leitura

Fonte de leitura

Álgebra, lógica e uma ideia de cada vez.

Código

Fonte do código

Navegação

Cursor de leitura, sem edição. h, j, k, l movem; / pesquisa nesta página. v seleciona caracteres; V seleciona linhas; y copia. Tab permite destacar ou comentar. Espaço ou Enter abre blocos recolhíveis. Nos cartões, h, j, k, l mudam o foco.

CSS personalizado

Ativa uma sugestão ou guarda o teu CSS. Só muda este navegador.

Usa --page, --text e --accent para acompanhar o tema.

Seletores e recuperação

.site-header, .prose, .course-card, [data-reading-history], [data-playground] e .page-actions.

Abre a aparência com a tecla vírgula ou no rodapé. Se o CSS esconder os controlos, abre esta página sem CSS personalizado.

Pesquisar

Escreve para pesquisar em todo o site.

para escolher · Enter para abrir · Esc para fechar

Copiar fórmula

A cópia automática não está disponível. Copia o LaTeX abaixo.

Pesquisar nesta página

Enter procura · n / N repetem · Esc cancela. Ctrl / Cmd K pesquisa todo o site.

Atalhos de teclado

Clica numa tecla para a mudar. Esc cancela. Backspace desativa.

PesquisarCtrl / Cmd K

Os atalhos não interferem enquanto escreves. Tab e Enter funcionam sempre.