Expressões Regular (Regular Expressions)¶

Uma expressão regular (regexp) é uma forma concisa e flexível de definir padrões (máscaras) a serem identificados em sequências de caracteres (Strings).

In [ ]:
# Utilizar o módulo de expressões regulares (re)
import re

Suponha que queiramos verificar se uma pessoa digitou um número de CPF que seja sintaticamente correto. Como sabemos, os números de CPF são compostos por 11 dígitos numéricos. Assim, podemos definir um padrão como:

"[0-9]{11}"

Nesta expressão regular temos:

  • o uso de colchetes possibilita definir uma lista de caracteres de interesse, não importando a ordem. Dentro deles, a expressão "0-9" significa um intervalo que vai do caracter "0" até o caracter "9". Assim, a expressão "[0-9]" é equivalente a "[0123456789]", ou seja, identifica qualquer um dos caracteres correspondentes a dígitos numéricos.
  • a expressão "{11}" é um modificador da expressão anterior, significando que desejamos exatamente 11 ocorrências de qualquer um dos caracteres descritos anteriormente.
In [ ]:
cpf = input("Digite seu número de CPF: ")
padrao_cpf = '[0-9]{11}'
if re.match(padrao_cpf, cpf):
    print("CPF ok")
else:
    print("CPF com problemas")

Note que se você digitar algo como "12345678901234afasdf", o resultado será "CPF ok", apesar de nitidamente vermos que isso não parece correto. Tal fato ocorre em Python porque a função match procura pelo padrão a partir do início da string. Como o valor digitado inicia com 11 dígitos numéricos, o padrão definido pela expressão regular está presente no valor digitado, de forma que o valor digitado condiz com o padrão, apesar de ser seguido por outros caracteres. Para corrigir isso, é necessário ajustar a expressão regular, incluindo o caracter "$" no final:

"[0-9]{11}$"

O uso do caracter "$" indica que no valor digitado não deve haver mais nenhum caracter após a parte que condiz com o padrão.

In [ ]:
# A expressão regular inclui o caracter "$" no final
cpf = input("Digite seu número de CPF: ")
padrao_cpf = '[0-9]{11}$'
if re.match(padrao_cpf, cpf):
    print("CPF ok")
else:
    print("CPF com problemas")

O padrão acima considera que o CPF tenha apenas dígitos numérico. Se o CPF for digitado sem seu formato habitual (ex: "123.456.789-01") o reconhecimento falhará visto que a expressão regular não prevê o uso de pontos e hífen. Um novo ajuste é necessário caso queiramos que este padrão seja reconhecido. Algo como:

"[0-9]{3}\.?[0-9]{3}\.?[0-9]{3}-?[0-9]{2}$"

In [ ]:
# Expressão regular que reconhece pontos e hífen no CPF
cpf = input("Digite seu número de CPF: ")
padrao_cpf = r'[0-9]{3}\.?[0-9]{3}\.?[0-9]{3}-?[0-9]{2}$'
if re.match(padrao_cpf, cpf):
    print("CPF ok")
else:
    print("CPF com problemas")

Note que o caracter "." foi precedido por uma contrabarra "\" na expressão regular. Isto é necessário pois o caracter ponto tem semântica específica em expressões regulares, significando "qualquer caracter". Assim, a expressão ".{5}" significa quaisquer cinco caracteres. Mas no nosso caso, nós queremos que seja de fato o ponto, e não um caracter qualquer. Para garantir esta interpretação é necessário precedê-lo por uma contrabarra.

Mas ao inserir a contrabarra nós acabamos gerando um conflito. Acontece que em Python a contrabarra já é utilizada em strings para identificar caracteres particulares. Por exemplo, na string "uma linha\noutra linha" a sequência "\n" indica uma quebra de linha (new line) (veja outros caracteres especiais). De forma a evitar esta dupla interpretação da contrabarra (em Python e na expressãor regular) é que sugere-se que as strings correspondentes à expressão regulares sejam precedidas da letra "r", significando raw string, ou seja, que as contrabarras não serão interpretadas pela linguagem Python, mas apenas pelo interpretador de expressões regulares.

O carater "?" inserido após os pontos e o hífen é outro modificador, indicando que o que vem antes pode ocorrer 0 (zero) ou 1 (uma) vez, mas não mais. Desta forma a expressão regular reconhece CPFs com ou sem pontos e hífen.

Outro exemplo do uso de expressões regulares é o reconhecimento de endereços eletrônicos (e-mail). No caso da UFSC, os endereços de email tem formatos como:

  • zzz.yyy@ufsc.br
  • zzz.yyy@grad.ufsc.br
  • zzz.yyy@pet.inf.ufsc.br

Abaixo é apresentado uma expressão regular que permite identificar estes padrões.

In [ ]:
# Expressão regular que reconhece e-mail UFSC
email = input("Digite seu endereço eletrônico: ")
padrao_email = r'[a-z][a-z.]+@([a-z]+\.)*ufsc\.br$'
if re.match(padrao_email, email):
    print("É um e-mail UFSC")
else:
    print("Não é um e-mail UFSC válido")

O padrão:

r'[a-z][a-z.]+@([a-z]+\.)*ufsc.br$'

indica que os endereços eletrônicos devem:

  • iniciar com uma letra minúscula: "[a-z]"
  • ser seguido de uma ou mais letras minúsculas ou caracter ponto: "[a-z.]+" (o modificador "+" significa uma ou mais ocorrências dos caracteres definidos imediatamente antes)
  • ser seguido pelo caracter "@": "@"
  • ser seguido por zero ou mais ocorrências de sequências de uma ou mais letras seguidas de ponto: "([a-z]+\.)*" (o modificador "*" significa zero ou mais ocorrências dos caracteres definidos imediatamente antes)
  • terminar com "ufsc.br": "ufsc\.br$"

Uso de expressões regulares para obter partes do texto¶

Além de reconhecimento de padrões, as expressões regulares podem ser utilizadas para separar partes de uma string. Para exemplificar, considere que polinômio: $-4x^5-x^4+2x$ que pode ser representado pela string: "-4x5-x4+2x". Podemos identificar e separar seus termos utilizando o método findall, conform exemplificado abaixo.

In [ ]:
# Identificação dos termos de um polinômio
padrao_termo = r'[-+0-9]*x[0-9]*'
polinomio = '-4x5-x4+2x'

termos = re.findall(padrao_termo, polinomio)
print(termos)

O mecanismo acima corretamente identifica os termos do polinômio (resultando numa lista), mas falha no caso do polinômio: $-4x^5-x^4+2x-10$ uma vez que o último termo não inclui a variável "x": o último termo não é identificado. Para identificá-lo é necessário alterar a expressão regular, a exemplo do descrito abaixo.

In [ ]:
# Identificação dos termos de um polinômio
padrao_termo = r'[-+0-9]*x[0-9]*|[-+0-9]+'
polinomio = '-4x5-x4+2x-10'

termos = re.findall(padrao_termo, polinomio)
print(termos)

Na expressão regular foi incluída no final a sequência "|[-+0-9]+" sendo que a barra vertical "|" corresponde a um OU lógico, ou seja, passam a ser reconhecidos o padrão à sua esquerda ou o padrão à sua direita.

Observe abaixo que foram incluídos pares de parênteses em torno das partes da expressão que reconhecem os coeficientes e os expoentes dos termos dos polinômios. Como resultado, o método findall não apenas reconhece os termos, mas também já os separa retornando-os na forma de tuplas.

In [ ]:
# Identificação dos coeficientes e expoentes dos termos de um polinômio
padrao_termo = r'([-+0-9]*)x([0-9]*)|([-+0-9]+)'
polinomio = '-4x5-x4+2x-10'

termos = re.findall(padrao_termo, polinomio)
print(termos)

Alguns outros métodos:¶

A lista de métodos para operar com expressões regulares inclui:

  • re.search(): similar ao match, com a diferença que o padrão é buscado em qualquer lugar da string, e não apenas a partir do início como é o caso do "match";
  • re.split(): divide uma string em pedaços com base no padrão definiçáo pela expressão regular;
  • re.sub(): substitui partes de uma string que condiz com um padrão por outra string;

Para saber mais sobre expressões regulares veja:¶

  • Documentação de Python
  • Mini tutorial