Índice

Regex, motherfucker

Neste artigo, faremos seus olhos chorarem sangue. De alegria.

Expressões regulares (regex ou regexp) são extremamente úteis na extração de informações de qualquer texto, procurando uma ou mais correspondências de um padrão de pesquisa específico (ou seja, uma string específica de caracteres ASCII ou unicode).

Os campos de aplicação variam da validação à análise/substituição de strings, passando pela tradução de dados para outros formatos e scraping na web.

Um dos recursos mais interessantes é que, depois de aprender a sintaxe, você poderá usar essa ferramenta em (quase) todas as linguagens de programação (JavaScript, Golang, Kotling, Java, C#, Python, Perl e muitos outras) com pouquíssimas diferenças entre em relação aos recursos e versões de sintaxe.

Vamos começar examinando alguns exemplos e explicações.

Tópicos Básicos

Âncoras — ^ e $

Regex Resultado
^Homer Corresponde a qualquer sequência que comece com Homer
Marge$ Corresponde a uma string que termina com Marge
^Bart Simpson$ Correspondência exata de string (começa e termina com Bart Simpson )
Lisa Corresponde a qualquer string que tenha o texto Lisa

Quantificadores — * + ? e

Regex Resultado
abc* Corresponde a uma string que tem ab seguida por zero ou mais c
abc+ Corresponde a uma string que tem ab seguida por um ou mais c
abc? Corresponde a uma string que tem ab seguida por zero ou um c
abc{2} Corresponde a uma string que tem ab seguida por 2 c
abc{2,} Corresponde a uma string que tem ab seguida por 2 ou mais c
abc{2,5} Corresponde a uma string que tem ab seguido por 2 até 5 c
a(bc)* Corresponde a uma string que tem a seguido por zero ou mais cópias da string bc
a(bc){2,5} Corresponde a uma string que tem a seguido por 2 até 5 cópias da string bc

Operador OR — | ou []

Regex Resultado
a(b|c) Corresponde a uma string que tem a seguido por b ou c (e captura b ou c)
a[bc] Igual ao anterior, mas sem capturar b ou c

Classes de caracteres — \d \w \s e .

Regex Resultado
\d Corresponde a um único caractere que é um dígito
\w Corresponde a um caractere de palavra (caractere alfanumérico mais sublinhado)
\s Corresponde a um caractere de espaço em branco (inclui guias e quebras de linha)
. Corresponde a qualquer caractere

Caracteres negados

Use o . com cuidado, pois muitas vezes a classe ou a classe de caractere negado (que abordaremos a seguir) são mais rápidas e precisas.

\d, \w e \s também apresentam suas negações com \D, \W e \S respectivamente.

Por exemplo, \D executará a correspondência inversa (corresponde a um único caractere sem dígito) em relação à obtida com \d.

Escapando literais

Regex power

Para ser considerado literalmente, você deve escapar os caracteres ^ . [ $ ( ) | * + ? { \ com uma barra invertida \, pois eles têm um significado especial. Ao escapar o caractere, você consegue encontrar um $ antes de um dígito .

É possível também encontrar caracteres não imprimíveis, como tabulações \t, novas linhas \n e retornos \r.

Flags

Uma regex geralmente é feita nesse formato /abc/, em que o padrão de pesquisa é delimitado por dois caracteres de barra /. No final, podemos especificar uma flag com os seguintes valores (que podem ser combinados):

  • g (global): não retorna após a primeira correspondência, reiniciando as pesquisas subseqüentes a partir do final da correspondência anterior
  • m (várias linhas): quando ativado ^ e $ corresponderão ao início e ao fim de uma linha, em vez de toda a string
  • i (insensível): torna a expressão inteira sem distinção entre maiúsculas e minúsculas (por exemplo /aBc/i econtraria AbC)

Tópicos intermediários

Agrupando e capturando — ()

Regex Resultado
a(bc) Os parênteses criam um grupo de captura com o valor bc
a(?:bc)* Usando ?: desativamos o grupo de captura
a(?<foo>bc) Usando ?<foo> colocamos um nome no grupo
Regex Yoda

Esse operador é muito útil quando precisamos extrair informações de strings ou dados usando sua linguagem de programação preferida. Quaisquer ocorrências múltiplas capturadas por vários grupos serão expostas na forma de uma matriz clássica: acessaremos seus valores especificando usando um índice no resultado da correspondência.

Se escolhermos colocar dar nome para os grupos que utilizaremos (como (?<foo>...)), poderemos recuperar os valores do grupo usando o resultado da correspondência como um dicionário/map em que as chaves serão o nome de cada grupo.

Expressões entre colchetes — []

Regex Resultado
[abc] Corresponde a uma string que possui a ou b ou c (o mesmo que a|b|c)
[a-c] O mesmo que o anterior
[a-fA-F0-9] Uma string que representa um único dígito hexadecimal, sem distinção entre maiúsculas e minúsculas
[0-9]% Uma string que possui um caractere de 0 a 9 antes de um sinal de %
[^a-zA-Z] Uma string que não possui uma letra de a à z ou de A à Z . Nesse caso, o ^ é usado como negação da expressão

Lembre-se de que dentro das expressões entre colchetes todos os caracteres especiais (incluindo a barra invertida \) perdem seus poderes especiais: portanto, não aplicaremos a regra de escape.

Combinações greedy e lazy

Regex death

Os quantificadores * + {} são operadores gananciosos (greedy), portanto expandem a correspondência o máximo possível através do texto fornecido. Por exemplo, <.+> corresponde a <div>simples div</div> em Este é um teste de <div>simples div</div>. Para capturar apenas a tag div, podemos usar um ? para torná-lo preguiçoso (lazy):

<.+?> corresponde a qualquer caractere uma ou mais vezes incluído dentro de < e >, expandindo conforme necessário

Observe que uma solução melhor deve evitar o uso de . em favor de uma regex mais rigorosa:

<[^<>]+> corresponde a qualquer caractere , exceto < ou > uma ou mais vezes incluídas dentro de < e >


Tópicos avançados

Limites - \b e \B

\babc\b corresponde a uma pesquisa somente palavras inteiras

\b representa uma âncora como sinal de intercalação (é semelhante a $ e ^) correspondendo a posições em que um lado é um caractere de palavra (como \w) e o outro lado não é um caractere de palavra (por exemplo, pode ser o início da string ou um caractere de espaço)

Ele tem uma negação, \B. Isso é, o resultado de todas as posições em que \b não corresponde e poderia estar se quisermos encontrar um padrão de pesquisa totalmente cercado por caracteres de palavra.

Back-references — \1

Regex Resultado
([abc])\1 Usando \1, ele corresponde ao mesmo texto que foi correspondido pelo primeiro grupo de captura
([abc])([de])\2\1 Podemos usar \2 (\3, \4, etc.) para identificar ao mesmo texto que foi correspondido pelo segundo (terceiro, quarto, etc.) grupo de captura
(?<foo>[abc])\k<foo> Colocamos o nome foo no grupo e o referenciamos mais tarde (\k<foo>). O resultado é o mesmo do primeiro regex

Look-ahead e Look-behind — (?=) e (?<=)

e(?=r) Corresponde a e apenas se for seguido por r, mas r não fará parte da correspondência geral da expressão regular (?<=r)i Corresponde a i apenas se for precedido por um r, mas r não fará parte da correspondência geral da expressão regular

Regex Resultado
e(?=r) Corresponde a e apenas se for seguido por r, mas r não fará parte da correspondência geral da expressão regular
(?<=r)i Corresponde a i apenas se for precedido por um r, mas r não fará parte da correspondência geral da expressão regular

E ao usar o operador de negação:

Regex Resultado
e(?!r) Corresponde a e apenas se não for seguido por r, mas r não fará parte da correspondência geral da expressão regular
(?<!c)i Corresponde a i apenas se não for precedido por um c, mas c não fará parte da correspondência geral da expressão regular

Ferramentas úteis

Regex, Neo
  • O Regex101 é um site que você consegue visualizar suas regexs de um jeito didático. Além de conseguir exportar trechos de código para sua linguagem favorita.

  • Fazer tabelas em markdown (como esse site é escrito) é complicado, mas graças ao Markdown Tables a tarefa fica mais fácil


Conclusão

O poder da regex não pode ser subestimado, jovem gafanhoto. Os campos de aplicação de regex podem ser múltiplos e tenho certeza de que você percebeu pelo menos uma dessas tarefas durante sua carreira de desenvolvedor:

  • validação de dados: por exemplo, verifique se uma data ou um e-mail são válidos
  • scraping de dados: especialmente scraping na web, encontre todas as páginas que contêm um determinado conjunto de palavras, eventualmente em uma ordem específica
  • disputa de dados: transformar “brutos” dados para outro formato
  • análise de string: por exemplo, pegue todos os parâmetros de URL GET, capture o texto dentro de um conjunto de parênteses
  • substituição de cadeia de caracteres: substitua , por ;, faça com letras minúsculas, etc.
  • realce de sintaxe, renomeação de arquivo e muitas outras coisas que envolvem strings

UPDATE: Escrevi um novo artigo com as regexs mais usadas, que você confere aqui: Regex úteis para o seu dia a dia