Menu

Regexp em Golang: match, find, grupos de captura e replace

Como usar expressões regulares em Go com o pacote regexp: MustCompile, MatchString, FindString e FindAllString, grupos de captura e grupos nomeados, ReplaceAllString e os limites da sintaxe RE2, como a falta de lookbehind.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

Compile uma vez, depois case

Escreva os padrões como raw strings entre crases. Em uma string normal entre aspas duplas, toda barra invertida precisa ser duplicada ("\\d+"), o que logo fica ilegível.

regexp.MustCompile causa panic se o padrão for inválido, que é o que você quer para um padrão no seu código-fonte: um erro de digitação falha na hora, na inicialização, e não na primeira requisição. Para padrões que vêm de usuários ou de configuração, use regexp.Compile e trate o erro:

re, err := regexp.Compile(userPattern)
if err != nil {
	return fmt.Errorf("bad pattern: %w", err)
}

Compile uma vez. Compilar é muito mais caro que casar. Um regexp.MustCompile dentro de uma função que executa a cada requisição ou a cada linha repete esse trabalho toda vez. Coloque o *regexp.Regexp compilado em um var de nível de pacote ou em um campo de struct. Uma regexp compilada pode ser usada por muitas goroutines ao mesmo tempo com segurança.

regexp.MatchString(pattern, s) compila e casa em uma chamada só. Serve para uma verificação avulsa e é desperdício dentro de um laço.

Encontrando casamentos

Os nomes dos métodos seguem um padrão: Find + All? + String? + Submatch? + Index?.

ParteSignificado
Alltodos os casamentos sem sobreposição; recebe um limite n (-1 para todos)
Stringtrabalha com uma string; sem ela, o método recebe e devolve []byte
Submatchdevolve também os grupos de captura
Indexdevolve offsets em bytes em vez do texto

FindString devolve "" quando não há casamento, o que não se distingue de casar uma string vazia. Quando o padrão pode casar um texto vazio, use FindStringIndex (devolve nil quando não há casamento) ou MatchString antes. As variantes All devolvem nil quando nada casa.

Grupos de captura

Parênteses capturam. FindStringSubmatch devolve o casamento inteiro no índice 0 e depois uma entrada por grupo:

Sempre verifique se é nil antes de indexar o resultado; sem casamento, m[1] causa panic.

(?:...) agrupa sem capturar, para alternância ou repetição: (?:ab)+. Grupos nomeados se escrevem (?P<nome>...) e, desde o Go 1.22, também (?<nome>...).

Substituindo

Uma armadilha clássica com referências a grupos: "$1x" é lido como um grupo chamado 1x, que não existe, e vira uma string vazia. Escreva "${1}x".

Para uma string fixa, strings.ReplaceAll, strings.Contains e strings.Split são mais simples e mais rápidos que uma regexp. Recorra ao regexp quando o texto que você procura tem um formato, e não um valor fixo.

Referência rápida da sintaxe

O Go usa a sintaxe RE2, que cobre os recursos conhecidos do estilo Perl:

SintaxeCasa com
.qualquer caractere exceto quebra de linha (com (?s), a quebra também)
\d \w \sdígito, caractere de palavra [0-9A-Za-z_], espaço em branco (só ASCII)
\D \W \Sas negações
[abc] [^abc] [a-z]classes de caracteres
\pL \p{Greek}classes Unicode: qualquer letra, qualquer caractere grego
* + ? {n,m}repetição, gulosa
*? +? ??repetição, preguiçosa
^ $início e fim do texto (da linha, com (?m))
\bfronteira de palavra (ASCII)
a|balternância
(?i)sem diferenciar maiúsculas e minúsculas daquele ponto até o fim do grupo que o contém; normalmente escrito no início do padrão

\d e \w só casam com ASCII. Para "qualquer letra em qualquer idioma", use \pL, e para qualquer dígito Unicode, \p{Nd}. regexp.QuoteMeta(s) escapa todos os caracteres especiais de s, o que você precisa ao montar um padrão a partir de entrada do usuário.

O que o RE2 não consegue fazer

A regexp do Go garante que o casamento leva tempo linear no tamanho da entrada. Recursos que quebrariam essa garantia não são suportados:

  • Sem lookahead nem lookbehind: (?=...), (?!...), (?<=...), (?<!...) são erros de compilação. Como o Go 1.22 passou a aceitar (?<nome>...) para grupos nomeados, um lookbehind agora falha com a mensagem confusa invalid named capture, como mostra o programa abaixo.
  • Sem backreferences: (\w)\1 para casar uma letra repetida não é possível.
  • Sem quantificadores possessivos nem grupos atômicos.

A solução alternativa é quase sempre a mesma: capture um pouco mais do que precisa e depois filtre ou fatie em Go. A recompensa é que uma regexp do Go não pode ser levada a um backtracking catastrófico. Um padrão como (a+)+$, que pode congelar um motor PCRE com uma entrada curta, executa em tempo linear aqui, o que importa quando o padrão ou a entrada vêm de fora.

Erros comuns

  • Compilar dentro de um laço ou de um handler. Compile uma vez em uma variável de nível de pacote.
  • Padrões entre aspas duplas. "\d" nem é uma string Go válida; use crases.
  • Esquecer as âncoras na validação. \d{5} casa dentro de "abc123456xyz". Use ^\d{5}$.
  • Indexar o resultado de um submatch sem verificar se é nil.
  • Esperar que \w ou \b entendam texto não ASCII. Use classes Unicode como \pL.
  • Usar regexp para strings fixas. O pacote strings é mais claro e mais rápido.

Perguntas frequentes

Como verificar se uma string casa com uma regex em Go?

Compile o padrão uma vez com regexp.MustCompile, escrevendo-o como raw string entre crases para que as barras invertidas não precisem de escape, e chame re.MatchString(s). Sem âncoras (^ e $), MatchString devolve true se o padrão casar em qualquer lugar da string.

O regexp do Go suporta lookahead e lookbehind?

Não. O regexp do Go usa a sintaxe RE2, que não tem lookahead, lookbehind nem backreferences. Em troca, o casamento executa em tempo linear no tamanho da entrada, então um padrão ou uma entrada maliciosos não conseguem travar o seu programa. Use grupos de captura e um pouco de código Go, ou divida a verificação em duas regexes.

Qual a diferença entre regexp.Compile e regexp.MustCompile?

Compile devolve o padrão compilado e um erro. MustCompile causa panic com um padrão inválido. Use MustCompile para padrões escritos no seu código-fonte, normalmente em uma variável de nível de pacote, para que um erro de digitação falhe na inicialização. Use Compile para padrões que vêm de usuários ou de arquivos de configuração.

Como pegar os grupos de captura de uma regex em Go?

Use FindStringSubmatch, que devolve um slice em que o índice 0 é o casamento inteiro e os índices 1, 2, ... são os grupos, ou nil se não houver casamento. Para todos os casamentos, use FindAllStringSubmatch(s, -1). Grupos nomeados (?P<year>\d{4}) podem ser encontrados com re.SubexpIndex("year").

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR