Usando Regex a seu Favor — Part 1

Uma rápida abordagem de como REGEX podem ser uteis para trabalharmos com padrãos em textos.

Everton Tomalok
8 min readOct 21, 2018

Decidi escrever este artigo, após perceber como Regex (uma ferramenta tão útil e poderosa ao trabalharmos com identificação de padrões em textos, data mining, pré processamento de texto, e diversas outras aplicações), é deixada de lado, por muitos.

Começarei falando de ferramentas auxiliares, que o Python nos oferece ao trabalhar com strings, que são nada mais, que uma sequência de caracteres. Por exemplo, a frase “Eu sou o Groot”, é uma string que contém 14 caracteres (sim, o espaço é considerado um caracter).

No Python, assim como em outras linguagens, a indexação irá começar pela posição 0, e não pelo 1. Então, a substring “Eu”, ocupa a posição 0 e 1, da string “Eu sou o groot”.

Sem mais delongas, vamos ao que interessa!

Métodos:

.find(‘substring’): Este método, procura por um caracter ou uma substring, em uma string, e irá retornar com a posição do primeiro caracter encontrado. Caso não exista, irá retornar o valor inteiro -1.

No trecho de código abaixo, iremos procurar na string, pela substring ‘sou’. Como ela existe na string, irá nos retornar o valor do indice do primeiro caracter. No caso, o ‘s’ de ‘sou’, tem seu inicio alocado a posição 3.

>> #         0123
>> string = "Eu sou o Groot"
>> string.find('sou')
>> 3

.split(): “Quebra” a string em diversas substrings, sempre que o caracter passado por parametro, for encontrado. O valor padrão, é o ‘espaço’.e o retorno do método, será uma list.

>> string = "Eu sou o Groot">> string.split()
>> ['Eu', 'sou', 'o', 'Groot']

.join(‘caracter’): Este método, faz o caminho inverso do split. Devemos passar uma list, que contenha um ou mais caracteres, ou substrings, que irá ligar todos os elementos desta mesma.

>> lista_string = ['Eu', 'sou', 'o', 'Groot']>> '-'.join(lista_string)
>> "Eu-sou-o-Groot"

.replace(t, u): Sempre que a substring/caracter ‘t’ for encontrada na string, o mesmo será trocado pela substring/caracter ‘c’. Caso não exista ocorrência na string, nada será alterado.

>> string = "Eu sou o Groot">> string.replace('sou', 'amo')
>> "Eu amo o Groot"

Let’s be friends Regex

Neste artigo, não irei me aprofundar muito na parte teórica e da estrutura de uma Regex, e sim desejo mostrar o potencial e utilidade da mesma.

Prometo, em um futuro próximo, desbravar este mar turbulento =P

Porém, vamos entender o básico, de uma Regex.

Expressões regulares permitem que padrões sejam “achados” ou “casados” dentro de strings. Ações como substituição podem ser feitas na string se a expressão regular “casar” com alguma parte da string. O módulo que cuida de expressões regulares no Python é o re.

Existem dois métodos para achar padrões em strings com o módulo re:

  • search e match.

Vamos dar uma olhada no search.

  • O método search do “re”, solicita dois parâmetros de entrada. O primeiro, é um string regex ( por isso o ‘sou’ tem um “r” à esquerda das aspas duplas, que sinaliza ao interpretador do Python, que esta string deve ser entendida como uma regex). O segundo parâmetro, é a string a qual desejamos realizar o scan, em busca do padrão passado.
>> import re>> string = "Eu sou o Groot">> resultado = re.search(r"sou", string)

Para extrairmos o resultado, devemos usar .group():

>> resultado.group(0)
>> 'sou'

O zero, deve ser passado como parâmetro, pois o .group(), irá agrupar todos os “casamentos” encontrados dentro da string.

Por exemplo, poderíamos passar ao método search, mais de um padrão de busca. Para que isso seja possível, utilizaremos os padrões dentro de “parenteses”.

>> string = "Eu sou o Groot">> resultado = re.search(r'(Gro)(ot)', string)>> resultado(1)
>> "Gro"
>> resultado(2)
>> "ot"

Porém, nem sempre teremos um “texto fixo” para ser procurado em uma string. Para isso, temos alguns auxiliares, que podem nos ajudar a extrair elementos de uma string.

Créditos ao Rodrigo Branas por esta imagem

Antes de prosseguirmos, precisamos entender os delimitadores ^ e $.

O ^(acento circunflexo) indica “começar”, ou seja, eu desejo que apenas strings que comecem com determinado padrões, devem ser recuperados.

Já o $ (dóllar), indica “terminar”, ou seja, eu desejo que a string termine com um determinado padrão.

>> string = "Eu sou o Groot"# Substring que comece com 'Eu'
>> resultado_inicio = re.search(r'^Eu', string)
>> resultado_inicio.group(0)
>> "Eu"
# Substring que termine com 'Groot'
>> resultado_final = re.search(r'Groot$', string)
>> resultado_final.group(0)
>> "Groot"

Sei que esses delimitadores, nos confundem ao primeiro contato. Porém, tentarei exemplificar melhor. Caso utilizarmos um regex para recuperar todas frases que comecem com “Eu” e terminem com “Groot”, independente do que venha no meio dos dois delimitadores, poderíamos utilizar essa notação.

Vamos ao exemplo:

>> string = "Eu sou o Groot"# String que comece com 'Eu' não importa o meio da sentença mas tem # que terminar com "Groot"
>> resultado = re.search(r'^Eu\s(.)*\sGroot$', string)
>> resultado.group()
>> 'Eu sou o Groot'

Vamos entender este trecho de regex r”^Eu\s(.)*\sGroot$”, antes de prosseguirmos:

  • Esta regex traduzida, significa -> Encontre toda string que comece com “Eu”, e que contenha um carácter “espaço” logo em seguida, e após o espaço, qualquer carácter é válido, e o final da string deve conter um caracter “espaço” e logo após a substring “Groot”.
  • O ponto (.) é o metacaracter citado acima, que representa o casamento com qualquer caracter (Ex.: A, b, c , 1, 2, _, @) .
  • O \s é o metacaracter que representa o caracter espaço.
  • O asterisco *, faz menção ao tamanho do padrão, que deve ser encontrado. Traduzindo, o asterisco, significa que o padrão deve conter no texto, 0 ou mais vezes, não limitando o tamanho máximo. Aproveitando o gancho, o metacaracter ponto de interrogação “?”, indica que o padrão entre parenteses, deve ocorrer 0 ou no máximo 1 vez. Já o metacaracter mais “+”, indica que o elemento deve ocorrer no minimo 1 vez, e não limita o máximo de ocorrências.

Agora que entendemos o que a regex significa, vamos mostrar na prática o que a mesma pode capturar:

>> string_2 = "Eu  não sou o Groot">> resultado = re.search(r'^Eu\s(.)*\sGroot$', string_2)# Nossa regex capturou a string
>> resultado.group()
>> 'Eu não sou o Groot'

Mais outro exemplo:

>> string_3 = "Eu não conheço o Groot">> resultado = re.search(r'^Eu\s(.)*\sGroot$', string_3)# Nossa regex capturou a string
>> resultado.group()
>> 'Eu não conheço o Groot'

Um exemplo em que nossa regex não pode capturar a string, pois nossa string não termina com “Groot”:

>> string_not_ecsiste = "Eu não conheço o Groot e não faço a mínima ideia de quem ele seja">> resultado = re.search(r'^Eu\s(.)*\sGroot$', string_not_ecsiste)# Nossa regex não capturou a string
>> resultado.group()
>> AttributeError: 'NoneType' object has no attribute 'group'

E o porquê dessa except, que foi levantada?

É bem simples. Por definição dos desenvolvedores do Python, o método search, sempre que não puder encontrar um “casamento” entre regex e string, irá retornar o singleton None, ao invés de um objeto “re”, que possui o método group. Essa except de tipo AttributeError, nos diz exatamente isso: “Não existe método ‘group’ em um objeto ‘NoneType’. ”

Para contornamos esse problema, podemos utilizar “if” e “else”, para não “quebrar” nosso programa.

>> string_not_ecsiste = "Eu não conheço o Groot e não faço a minima ideia de quem ele seja">> resultado = re.search(r'^Eu\s(.)*\sGroot$', string_not_ecsiste)>> if resultado is None:
>> print('Nada encontrado')
>> else:
>> resultado.group()
>> 'Nada encontrado'

Vamos entender mais sobre delimitação de caracteres em uma regex

Existe outra forma de delimitarmos o tamanho de um padrão contido em uma regex, para que uma substring seja extraída. Podemos utilizar a notação {n,m}, onde n é o número mínimo de elementos, e m o número máximo.

Por exemplo, na string “O capitão América é mais legal que o Homem de Ferro”, eu desejo recuperar substrings que comecem com ‘c’ e tenham no mínimo 7 caracteres, e no máximo 8 caracteres. Utilizaremos o metacaracter “\w” para indicar que após o “c”, qualquer carácter alfa numérico, poderá ser incorporada na substring extraída.

>> text= "O capitão América é mais legal que o Homem de Ferro"
>> r = re.search(r"c\w{7,8}", text)
>> r.group(0)
>> "capitão"

Com este padrão de regex, conseguimos recuperar “capitão”, pois esta substring, “casou” com o que desejávamos recuperar.

Caso você nunca tenha ouvido falar de regex, ou deseja se aprofundar no conhecimento de regex (selo de recomendação Everton!), você pode visualizar a documentação ‘re’ oficial do Python, a qual é bem didática e de fácil entendimento! Também recomendo assistir a este vídeo do Rodrigo Branas, o qual, com sua excelente didática, mostra quão fácil é lidar com REGEX.

Agora, que sabemos o básico, vamos mais adiante.

Exemplos Práticos usando Regex

1 — Recuperando telefones:

Abaixo, esta listada uma regex r’\(?\d{2,3}\)?\.?\s*\d{4,5}-?\.?\s*\d{4}’, que é capaz de recuperar qualquer dos números nacionais listados, tanto celular, quanto telefones fixos, com 3 ou 2 dígitos de DDD ( 11 ou 011 por exemplo) :

  • (xx) yyyyy-wwww
  • xx yyyyy wwww
  • xxyyyyywww
  • (xx)yyyyywwww
  • (xx)yyyyy.wwww
  • (xx) yyyyy-wwww
  • xx yyyy wwww
  • xxyyyywww
  • (xx)yyyywwww
  • (xx)yyyy.wwww
>> string = "O meu telefone para contato é: (011) 99999-8888... você pode me ligar depois das 17h.">> finder = re.search(r'\(?\d{2,3}\)?\.?\s*\d{4,5}-?\.?\s*\d{4}', string)
>> finder.group()
>> "(011) 99999-8888"

Um adendo ao barra invertida \ que informa ao interpretador do Python, que o carácter a seguir deve ser escapado (não deve ser considerado como seu valor de carácter). Então o “\(“ e o “\)” informados na regex, são exatamente isso, que os parenteses devem ser considerado em seu valor de carácter, e não como o metacaracter de inicio de padrão “(“ , e o de final de padrão “)”.

Utilizando esta técnica, conseguimos recuperar telefones que comecem com (xx)

Outro exemplo, é escapar o ponto final (.), que como visto anteriormente, significa qualquer carácter, em seu significado de metacaracter. E no nosso caso, desejamos utilizar o carácter ponto exclusivamente, para que possamos capturar telefones xx.yyyy.wwww

Então esta é a técnica, escapando metacaracteres para serem utilizados com seu valor de caracter utilizando \, para capturar telefones com ponto entre os números.

Finalizando!!!

Este foi o primeiro tema, introdutório a REGEX. Poderia escrever um livro, só de possíveis utilizações delas.

Em um próximo post, irei mostrar um pouco mais sobre regex, e sua utilização avançada.

Caso surjam dúvidas, estou disponível para ajudar.

Abraços!

--

--

Everton Tomalok

I’m a Data Scientist / Data Engineer and Python Developer, who loves math and to discover new technologies. In love with NLP and Computer vision.