Regex pra quem não sabe Regex


Convenhamos: Regex é uma parada contra-intuitiva. Entretanto, não é impossível de aprender. Se teve jeito pra mim, tem pra qualquer pessoa. :) Nesse post, uma introdução ao Regex com exemplos simples do dia a dia.


Uma vez um programador me falou que o ideal era aprender coisas sob demanda. Sabendo que existe um mar de coisas para aprender no mundo da computação, essa estratégia faz todo sentido pra mim. Porém durante algum tempo eu usei uma estratégia meio errada nesse “aprendizado sob demanda”. Quando eu precisava resolver algum problema sobre algo que eu não sabia, a estratégia era o famoso dar murro em ponta de faca ou mandar o freestyle.

Com Regex não foi diferente. Mas em algum ponto eu dediquei um pouquinho de tempo pra aprender alguns conceitos básicos e isso já foi o suficiente para me dar a autonomia necessária pra resolver problemas do dia a dia de maneira muito mais consciente e eficiente.

Vamo no básico?

Regex é uma abreviação para expressões regulares. Para formar essas expressões (ou estruturas) você tem a sua disposição meta-caracteres. Os meta-caracteres são códigos que podem filtrar pelo tipo de dado, por um intervalo e/ou pela frequência. Pra deixar o aprendizado mais prático, vamos aos exemplos!

Digamos que você quer filtrar todos os números do trecho abaixo:

A Lei Áurea foi sancionada em 13 de maio de 1888. Mas, 130 anos depois, dados do Observatório Digital do Trabalho Escravo no Brasil revelam que 1,73% (ou 613) dos 35.341 trabalhadores resgatados da escravidão no país entre 2003 e 2017 foram vítimas desse crime ao menos duas vezes.

Fonte: https://observatorioescravo.mpt.mp.br/

Para filtrar todos os números você pode usar \d (para os dígitos individuais como 1, 3, 1, 8, 8, 8...) ou \d+. O + indica que o match pode acontecer uma ou ilimitadas vezes. Usando o \d+ você terá como resultado 13, 1888, 130.... Se quiser ir testando, copia e cola esse trecho no regex101 e segue comigo.

Ao invés dos números, vamos tentar os anos apenas. Você pode usar {} para indicar quantas vezes esse padrão tem que aparecer para a expressão ser válida. Sabendo que os anos tem 4 dígitos nossa expressão será \d{4}. Dessa forma teremos 1988, 2003 e 2017 como resultado.

Vamos buscar datas completas, como 13 de maio de 1888. A lógica seria: <aqui o dia> de <aqui o mês> de <aqui o ano>. Já sabemos que precisamos de dois dígitos para o dia (\d{2}) e quatro dígitos para o ano (\d{4}). Como validar o trecho de <aqui o mês> de então?

Pra isso podemos usar várias abordagens. Vamos por partes. Primeiro vamos buscar os espaços em branco. Isso pode ser feito com \s. Nesse caso, vai dar match com apenas 1 espaço. Se não nos importamos em quantos espaços, adicionamos o +. Até agora temos: \d{2}\s+de\s+. Está vendo o de aí no meio? Deixamos ele porque queremos, literalmente, encontrar um de entre essas estruturas.

Para encontrar o mês é mais tranquilo. Podemos simplesmente buscar por palavras. Você pode fazer isso utilizando a sintaxe [A-Z] (o que quer dizer que a busca é por caracteres entre A e Z). Atenção: essa sintaxe é case sensitive. Para buscar por letras maiúsculas e minúsculas você pode usar [a-zA-Z]. Voltando ao exemplo, ficaria: \d{2}\s+de\s+[a-zA-Z]+\s+de\s+\d{4}. Veja que tem um + logo após [a-zA-Z] para pegar um conjunto de caracteres; em outras palavras, uma palavra. :) Você pode substituir [a-zA-Z] por \w também - dá no mesmo.

Tentar buscar URLs em um texto é um exercício bom também. Vamos mudar o nosso exemplo pro trecho abaixo:

Fonte: https://observatorioescravo.mpt.mp.br/ Fonte: http://observatorioescravo.mpt.mp.br/ Outras coisas aqui: http://observatorioescravo.mpt.mp.br/ Fonte: observatorioescravo.mpt.mp.br

Digamos que eu quero filtrar as URLs que tem http ou https no início. Uma maneira de fazer é Fonte: ((https|http):\/\/)?.*$. Nessa expressão eu agrupo por http ou https e faço com que essa estrutura https:// seja opcional usando ?. Apenas as URLs com Fonte: no início darão match.

Existem várias maneiras de resolver o mesmo problema.

Dando nomes aos bois

Nós aplicamos muitas coisas do mundo Regex nesse pequeno exemplo. Vamos dar nomes a elas:

  • Quantifier: quantifica quantas vezes (ou a frequência) que o padrão se repete

    • Mínimo n, máximo m (nos exemplos usamos apenas o mínimo): {n,m}
    • Opcional: ?
    • Uma ou mais vezes: +
    • Zero ou mais vezes: *
  • Âncoras: especifica onde o match deve ocorrer

    • Início: ^
    • Fim: $
  • Classes: um conjunto do mesmo padrão

    • \d todos os dígitos
    • \s espaço em branco
    • \w palavras (conjunto de caracteres)

Outros exemplos

Aqui deixo alguns exemplos que uso no meu dia a dia:

Validando campos

O uso de regex para validação de coisas como CPF, CNPJ e datas é bem comum. Aqui trago um exemplo de placas de carros brasileiras:

Exemplo: KMG-8089

[A-Z]{3}-\d{4}

Você já sabe que precisa de três letras ([A-Z]{3}), um hífen e quatro dígitos (\d{4}). Só juntar. :)

Buscando endpoints acessados nos logs

Digamos que eu precise buscar entre os logs registros de certo endpoint. Algo como: sales_appointment/sales_visit/22615/cancel. O ID será diferente sempre, o que torna o uso de regex ideal para essa busca.

Podemos pegar todo o conteúdo entre /sales e /cancel usando .* (o que significa . todos os caracteres e * várias vezes). Além disso, usamos \d* para indicar que queremos dar match em vários dígitos, não importa quantos. Vale notar que estamos escapando a barra aqui, para indicar que queremos buscar literalmente por /.

\/sales.*\/\d*\/cancel

Nesse caso, o trecho sales_visit/22615/cancel dará match porque estamos buscando o padrão sales_visit/<números>/cancel.

Buscando exceções

Se você quiser buscar exceções como: Caused by: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException você pode usar o padrão:

^Caused by:.*

Nessa linha estamos explicitando que queremos que Caused by esteja no início da linha e que após : nós não nos importamos. Pode dar match em tudo. Se quiser que seja até o final da linha e não do trecho basta adicionar $.

Buscando imports na documentação do Python

Digamos que você quer encontrar imports na página do módulo pprint do Python. Você pode usar o que já aprendeu até aqui para encontrar imports que estejam na estrutura from blabla import bla.

from\s+.+\s+import\s+\w+

Filtrando uma linha com base nas 3 anteriores e 3 posteriores

Esse exemplo surgiu no Telegram da comunidade Python-MG. A pessoa gostaria de filtrar a linha testando 123 testando com base no conteúdo das três anteriores e as três posteriores.

testando
testando
testando
testando
testando 123 testando
testando
testando
testando
testando

O Jeff Andrade deu a dica de como ficaria: (?:.*\n){3}.*testando 123*.(?:.*\n){3}. Você pode testar esse exemplo no Pythex.

Onde usar

Editores de texto, IDEs, terminal… Você pode usar regex em quase todo lugar. Quer usar regex para buscar informações em uma página web extensa? Também pode: Regex Find-in-Page Tool. Quer usar regex como linter? Pode também (relint).

Assim como várias linguagens de programação Python também tem um módulo para o uso de expressões regulares. Se quiser uma outra referência em pt-br, aqui um texto da Escola de Dados sobre como Expressão regular pode melhorar sua vida.

Todas as informações desse texto eu tenho em uma notinha para lembrar sempre que precisar. Espero que tenham gostado. Até a próxima!


comments powered by Disqus