Skip to content
Back to Blog
developerregex-testerregexweb-development

Lookahead e Lookbehind em Regex: Explicados com Exemplos

Lookahead e lookbehind são recursos de regex que muitos copiam mas nunca entendem. Aqui são explicados com exemplos reais que você pode testar ao vivo em diferentes sabores.

SZ
Founder, Molixa
11 min read
Compartilhar
Lookahead e Lookbehind em Regex: Explicados com Exemplos
Table of contents9 sections

Lookahead e lookbehind em regex são afirmações de largura zero: elas verificam se um padrão aparece ou não ao lado da sua correspondência, sem consumir caracteres ou incluí-los no resultado. Lookahead (?=...) verifica o que vem depois, lookbehind (?<=...) verifica o que vem antes, e ambos têm formas negativas para "não seguido por" e "não precedido por".

A palavra "largura zero" é a parte que confunde todo mundo. Um token regex normal corresponde a um caractere e move o cursor para frente. Uma afirmação corresponde a uma posição. Ela olha para o texto ao redor, retorna verdadeiro ou falso e deixa o cursor exatamente onde estava. É por isso que você pode empilhar vários lookaheads no mesmo lugar, que é o truque por trás de quase todo padrão de validação de senha que você já colou.

As Quatro Asserções de Lookaround#

Existem exatamente quatro lookarounds, divididos claramente em dois eixos: direção (à frente ou atrás) e polaridade (positiva ou negativa). Quando você os vê em uma grade, a sintaxe deixa de parecer arbitrária.

AsserçãoSintaxeSignificadoPortuguês claro
Lookahead positivo(?=...)A correspondência deve ser seguida por ..."é seguido por"
Lookahead negativo(?!...)A correspondência não deve ser seguida por ..."não é seguido por"
Lookbehind positivo(?<=...)A correspondência deve ser precedida por ..."vem depois de"
Lookbehind negativo(?<!...)A correspondência não deve ser precedida por ..."não vem depois de"

Os pontos dentro de cada asserção são um subpadrão normal. Eles são avaliados, o motor observa se corresponderam e depois descarta o movimento do cursor. Nada dentro de um lookaround aparece na sua correspondência ou nos seus grupos de captura.

Dica: se você quiser confirmar que uma asserção é realmente de largura zero, observe o comprimento do texto correspondido. Um padrão composto apenas por lookarounds corresponde a uma string vazia em uma posição. A saída interessante é onde ele correspondeu, não o quê.

Positive Lookahead: Corresponder Algo Seguido por Outra Coisa#

Digamos que você tenha preços como 42USD, 99EUR e 7GBP, e queira apenas o número que é imediatamente seguido por USD, sem capturar o código da moeda.

\d+(?=USD)

Contra 42USD 99EUR 7USD, isso corresponde a 42 e 7. O USD é necessário para que a correspondência ocorra, mas nunca é capturado, então você obtém números limpos que pode analisar diretamente. Compare com \d+USD, que força você a remover o USD depois.

Esse é o valor total do lookahead: uma condição que controla a correspondência sem se tornar parte dela.

Negative Lookahead: Corresponder Não Seguido Por#

Inverta a asserção para (?!...) e você obtém "corresponder a algo que não é seguido por outra coisa." Um caso clássico é corresponder uma palavra que não é seguida por um sufixo específico.

\bcat(?!alog)\b

Contra cat catalog category cats, isso corresponde ao cat isolado e ao cat dentro de cats (porque cats não é catalog), mas ignora catalog. Os limites de palavra \b impedem que corresponda ao cat enterrado em texto não relacionado.

O negative lookahead também alimenta padrões "corresponder a uma linha que não contém X" quando você ancora:

^(?!.*ERROR).*$

Isso corresponde a qualquer linha completa que não contenha a palavra ERROR em lugar nenhum. O lookahead varre a linha inteira primeiro; se encontrar ERROR, a asserção falha e a linha é ignorada. Isso é genuinamente útil para filtrar logs.

Lookbehind: Verificando o que vem antes#

O lookbehind faz o mesmo trabalho na direção oposta. O lookbehind positivo (?<=...) exige um padrão anterior; o lookbehind negativo (?<!...) proíbe um.

Um caso comum é extrair um valor que segue um símbolo de moeda, sem capturar o símbolo:

(?<=\$)\d+(\.\d{2})?

Em Preço: $42.50 e grátis, isso corresponde a 42.50 e deixa o $ fora do resultado. O lookbehind negativo é igualmente útil. Para corresponder a um número que não é precedido por um cifrão:

(?<!\$)\b\d+\b

Isso corresponde a números soltos como 2024 enquanto ignora valores monetários como $99. O lookbehind é onde as diferenças entre implementações começam a aparecer, que é a próxima coisa que você precisa saber antes de enviar um padrão.

A Armadilha da Portabilidade: Lookbehind Não é Universal#

Esta é a parte que a maioria dos tutoriais ignora, e é a parte que quebra o código em produção. Lookahead é suportado em quase todos os lugares. Lookbehind não é, e mesmo onde existe, as regras sobre se pode ter comprimento variável diferem entre mecanismos.

Engine / flavorLookaheadLookbehindLookbehind de comprimento variável
JavaScript (ES2018+)SimSimSim
PCRE / PCRE2 (PHP, muitas ferramentas)SimSimNão (apenas comprimento fixo)
Python (re)SimSimNão (apenas comprimento fixo)
Python (módulo regex)SimSimSim
Go (regexp, RE2)NãoNãoNão

Duas coisas se destacam. Primeiro, o pacote regexp padrão do Go usa o mecanismo RE2, que deliberadamente não tem lookaround, porque RE2 garante correspondência em tempo linear e lookarounds quebram essa garantia. Se você escrever (?<=\$) em Go, não compilará. Você reestrutura com grupos de captura.

Segundo, "lookbehind de comprimento fixo" significa que PCRE e o re embutido do Python rejeitam um lookbehind cuja largura pode variar. (?<=cat|dog) funciona porque ambas as alternativas têm 3 caracteres; (?<=cats?) falha nesses mecanismos porque a correspondência pode ter 3 ou 4 caracteres de largura. JavaScript e o módulo regex de terceiros do Python permitem lookbehind de comprimento variável, então o mesmo padrão que funciona no console do seu navegador pode lançar um erro em um backend Python.

Aviso: "funciona no regex101" não é o mesmo que "funciona na minha linguagem." regex101 permite que você escolha um flavor, mas é fácil testar sob PCRE e depois enviar para um serviço Go ou Python que rejeita o padrão. Sempre confirme com o mecanismo alvo real.

Um Caso de Uso Real: Validação de Política de Senha#

O lugar mais comum onde desenvolvedores encontram lookahead é nas regras de senha, e este é o exemplo mais claro de empilhamento de asserções de largura zero. Suponha que sua política seja: pelo menos 8 caracteres, com pelo menos uma letra minúscula, uma letra maiúscula e um dígito.

^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$

Analisando da esquerda para a direita:

  • ^ ancora ao início da string.
  • (?=.*[a-z]) afirma que em algum lugar à frente há uma letra minúscula. O cursor não se move.
  • (?=.*[A-Z]) afirma que existe uma letra maiúscula, novamente a partir do início.
  • (?=.*\d) afirma que existe um dígito.
  • .{8,}$ finalmente consome 8 ou mais caracteres até o final.

Cada lookahead verifica independentemente toda a string a partir da mesma posição inicial, porque nenhum deles moveu o cursor. É exatamente por isso que você pode expressar "deve conter todos estes, em qualquer ordem" como uma lista plana de asserções. Sem lookahead, você precisaria de uma alternância feia cobrindo todas as ordenações possíveis.

Para proibir espaços, adicione um lookahead negativo: (?!.*\s). Para exigir um caractere especial, adicione (?=.*[!@#$%^&*]). O padrão cresce uma asserção por regra, o que é muito mais legível que a alternativa.

Passo 1: Construa as asserções uma regra de cada vez#

Comece com as âncoras e o comprimento, depois adicione um único lookahead e confirme seu comportamento antes de adicionar o próximo. Cole o padrão parcial em um testador de regex gratuito e teste uma string que deve passar e uma que deve falhar. Se você adicionar todas as quatro asserções de uma vez e algo der errado, não saberá qual está errada.

Passo 2: Teste os casos limite, não apenas o caminho feliz#

Uma regex de senha que aceita Abcdef12 não está comprovadamente correta. Teste as strings que devem falhar: uma string toda em minúsculas, uma sem dígito, uma com exatamente 7 caracteres. Confirme que cada uma é rejeitada pelo motivo certo. Observar o destaque ao vivo em um testador mostra se a falha veio do comprimento ou de uma asserção ausente.

Passo 3: Fixe o sabor antes de copiar o padrão#

Depois que passar, configure o testador para o mecanismo que você realmente usará (JavaScript, PCRE, Python ou Go) e execute os mesmos casos novamente. Se você usou um lookbehind de comprimento variável ou qualquer lookaround no Go, é aqui que você descobre, no editor, e não em uma build de CI com falha.

Lookahead vs Grupos de Captura: Quando Usar Cada Um#

Uma fonte frequente de confusão é quando usar um lookahead em vez de um grupo de captura comum, já que ambos permitem "encontrar X perto de Y". A diferença está no que acaba na sua correspondência.

  • Use um grupo de captura (...) quando quiser manter o texto ao redor e extrair uma parte dele. A correspondência inteira inclui tudo; o grupo isola uma parte.
  • Use um lookaround quando o texto ao redor for apenas uma condição e você não quiser que ele apareça no resultado. A correspondência é apenas o alvo.

Concretamente, (\d+)USD corresponde a 42USD e captura 42 no grupo 1. \d+(?=USD) corresponde apenas a 42 e não captura nada. Ambos fornecem 42, mas o primeiro mantém USD na correspondência geral e o segundo não. Se você estiver fazendo uma busca e substituição e quiser deixar USD intocado, o lookahead é mais limpo, pois não há nada para recolocar.

Para trabalhos com padrões mais complexos, nosso guia sobre como dominar expressões regulares com um testador de regex online aborda grupos, âncoras e flags junto com lookarounds.

Folha de Dicas de Referência Rápida#

Mantenha isto ao lado do seu editor:

  • (?=foo) corresponde a posição seguida por foo
  • (?!foo) corresponde a posição não seguida por foo
  • (?<=foo) corresponde a posição precedida por foo
  • (?<!foo) corresponde a posição não precedida por foo
  • Lookaheads têm largura zero: eles afirmam, nunca consomem.
  • Empilhe lookaheads em ^ para exigir múltiplas condições em qualquer ordem.
  • Lookbehind não é suportado em Go (RE2) e deve ter comprimento fixo em PCRE e no re nativo do Python.

Quando precisar validar ou desembaraçar um padrão, cole-o no testador de regex Molixa e veja as correspondências e grupos atualizarem ao vivo, depois alterne os sabores para confirmar a portabilidade. Se também estiver lidando com payloads de API, o formatador JSON é útil para limpar respostas antes de aplicar regex, e se você já usou regex101, nossa alternativa gratuita ao regex101 explica as diferenças.

Perguntas Frequentes#

Qual é a diferença entre lookahead e lookbehind em regex? Lookahead (?=...) verifica o texto que vem depois da posição atual, enquanto lookbehind (?<=...) verifica o texto que vem antes. Ambos são asserções de largura zero, ou seja, testam uma condição sem incluir o texto ao redor na correspondência. Cada um também tem uma forma negativa, (?!...) e (?<!...), para "não seguido por" e "não precedido por."

O JavaScript suporta lookbehind em regex? Sim. O JavaScript suporta lookbehind e lookbehind de comprimento variável desde o ES2018, então (?<=\$)\d+ funciona em navegadores modernos e Node. Ambientes mais antigos anteriores ao ES2018 não suportam; se você precisar atingir um ambiente legado, teste lá ou reestruture com um grupo de captura em vez de lookbehind.

Por que meu lookbehind funciona em JavaScript, mas falha em Python? Provavelmente é de comprimento variável. O módulo re embutido do Python só permite lookbehind de comprimento fixo, então (?<=cats?) falha porque a largura varia. O JavaScript permite lookbehind de comprimento variável, então o mesmo padrão funciona lá. Use o módulo de terceiros regex no Python para suporte a comprimento variável, ou reescreva a asserção para largura fixa.

Como usar lookahead para validação de senha? Empilhe um lookahead positivo para cada regra no início do padrão e depois consuma os caracteres. Por exemplo, ^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$ exige uma letra minúscula, uma letra maiúscula, um dígito e um comprimento mínimo de 8. Como os lookaheads têm largura zero, cada um varre independentemente toda a string a partir da mesma posição.

O Go suporta lookahead e lookbehind em regex? Não. O pacote regexp padrão do Go usa o motor RE2, que não suporta lookaround, por design, para garantir tempo de execução linear. Padrões com (?=...) ou (?<=...) não compilarão em Go. Reestruture a lógica com grupos de captura ou use um binding PCRE de terceiros se realmente precisar de asserções.

Quando devo usar lookahead em vez de um grupo de captura? Use lookahead quando o texto vizinho for apenas uma condição e você não quiser incluí-lo no resultado. Use um grupo de captura quando quiser o texto ao redor na correspondência e precisar isolar uma parte dele. Por exemplo, \d+(?=USD) retorna apenas o número, enquanto (\d+)USD retorna o número, mas mantém USD na correspondência geral.

developerregex-testerregexweb-development

More from Molixa

Try Molixa Tools

50+ free AI tools for content creation, SEO, coding, and more. No signup, no watermark.

Explore all tools
Lookahead e Lookbehind em Regex Explicados | Molixa | Molixa