A maior parte de um regex casa caracteres. Âncoras são diferentes: elas casam uma posição entre caracteres, e não consomem nada. É isso que permite dizer "no início da linha" ou "na borda de uma palavra" em vez de apenas "em algum lugar".
As âncoras comuns
^casa o início da string, e$casa o fim.^abc$casa apenas a string exataabc, nãoxabcx. Sem elas, um padrão pode casar qualquer substring, que é a causa mais comum de um regex casar mais do que o pretendido.\bcasa um limite de palavra, a posição entre um caractere de palavra e um caractere que não é de palavra (ou o início ou o fim da string).\bcat\bcasacatcomo palavra inteira, mas não ocatdentro decategory.\Bé o oposto: uma posição que não é um limite de palavra.- Alguns dialetos também oferecem
\Ae\zpara o início e o fim absolutos da string, que passam a importar quando o modo multilinha entra em cena.
Multilinha muda o que ^ e $ significam
Por padrão ^ e $ ancoram na string inteira. Ligue o modo multilinha (a flag m) e eles ancoram no início e no fim de cada linha, casando em cada quebra de linha. É isso que você quer ao varrer um bloco de texto linha a linha, e uma surpresa quando você não esperava, porque um padrão que casava uma vez agora casa em cada linha.
Onde elas mordem
Dois erros se repetem. Primeiro, colocar ^ ou $ no meio de um padrão por acidente, o que só pode casar em uma borda de linha e por isso normalmente não casa nada. Segundo, presumir que \b entende o seu alfabeto: em muitos motores, um caractere de palavra é só ASCII, então \b cai no lugar errado em torno de letras acentuadas ou outros scripts, a menos que você habilite o modo Unicode. Quando um casamento de "palavra inteira" se comporta de forma estranha com texto que não é inglês, a definição de limite de palavra é a primeira coisa a verificar.
Um padrão sem âncora é uma busca de substring vestida de validador
^ e $ são a diferença entre "esta entrada é um identificador válido" e "esta entrada contém um em algum lugar". Uma regex de validação escrita sem âncoras aceita tudo que deveria rejeitar, desde que a parte aceitável apareça em qualquer ponto da string.
A falha é silenciosa porque os testes passam: uma entrada válida casa, e o desenvolvedor segue em frente. Ninguém testa ../../etc/passwd?id=12345 contra um padrão feito para aceitar um id numérico — e essa string contém um id numérico.
Duas armadilhas ficam ao lado. Em modo multilinha o $ casa antes de uma quebra de linha, então um \n final escapa de um padrão que parece ancorado — \A e \z são as formas inequívocas. E um validador deve testar a entrada inteira, não buscá-la: muitas linguagens oferecem as duas, e a chamada segura é aquela cujo nome significa casar por completo.