Como validar email com expressão regular
Quando você precisa fazer validação de um email programáticamente, como você faz? Pega uma biblioteca qualquer disponível na internet ou escreve a sua própria? Em ambos os casos, você pode estar resolvendo o problema de forma errada. Por quê? Muito simples, em ambos os casos você pode estar caindo em uma cilada!
Você não espera por isso (deveria!) mas muitas das soluções de validação de email com expressão regular disponíveis na web estão incorretas. Algumas acusam emails perfeitamente válidos como inválidos assim como aceitam emails inválidos. Esse problema ficou bem evidente para mim esses dias quando utilizando uma conhecida biblioteca javascript de validação de formulários (não vou citar). A validação de formulários da mesma estava quebrada. Tudo bem que isso não é um grande problema quando você implementa validação no lado do servidor também (que é o indicado), mas gera todo um mal-estar saber que há uma falha conhecida no seu site. Esse problema me motivou a escrever essa postagem.
Como deve ser um email válido?
Antes de ver como validar seu email é importante conhecer como deve parecer um email válido. Um email válido tem até 256 caracteres e é formado por duas partes separadas por um caractere arroba (@), a parte local e o domínio. ex: local@domínio. Vejamos os critérios de formação de ambos:
A parte local deve seguir os seguintes critérios:
- Deve ter até 64 caracteres de comprimento.
- Pode conter caracteres de a-z em caixa baixa, caixa alta ou ambos.
- Pode conter números de 0 a 9.
- Quaisquer destes caracteres são válidos:! # $ % & ' * + - / = ? ^ _ ` { | } ~
- O caractere '.' é permitido desde que não inicie nem termine a palavra ou ocorra duas vezes em sequência.
- Pode estar contido entre aspas duplas como em "Joe Doe"@dominio.com. Quando entre aspas duplas, caracteres não permitidos podem ser utilizados.
A parte domínio deve seguir os seguintes critérios:
- Pode conter caracteres de a-z em caixa baixa, caixa alta ou ambos.
- Pode conter números, caractere hífen e caractere ponto.
- Todas as sequências de caracteres entre pontos são chamadas etiquetas, sendo que cada etiqueta deve ter até 63 caracteres.
- Etiquetas podem iniciar com letras ou números apenas.
- O domínio deve conter até 255 caracteres.
- O domínio pode ser um endereço IP (não aconselhado!)
Ufa! Muitas regrinhas para um conjunto tão pequeno de caracteres, não é mesmo? Note que duas das regras citadas não podem ser reforçada com expressão regular. Digo quais são daqui a pouco.
Implementando uma solução
Meu negócio é python ponto. Isso quer dizer que a solução que eu vou colocar também é em python. Também vou colocar uma solução em javascript pois alguém pode achar útil. Se alguém proficiente em outra linguagem desejar colocar um porte da solução em sua linguagem predileta, sinta-se a vontade.
Primeiro, precisamos ter em mente que biblioteca usar para a validação com expressão regular. O python, por meio de sua filosofia batteries included, já te fornece a biblioteca re para trabalhar com expressões regulares. Basicamente, vamos precisar apenas dela. Vejamos:
# -*- coding:utf-8 -*-
import re # biblioteca para validação de regex
# caracteres não-alfanuméricos aceitos na parte local
local_char = r'\!\$\*\+\-\?\^\_\{\|\}\=#/\'~\%&`'
# parte local
local_word = r'[a-z0-9%(char)s][a-z0-9%(char)s.]{0,62}[a-z0-9%(char)s]?' % {'char':local_char}
domain_label = r'[a-z0-9][a-z0-9\-]{0,61}[a-z0-9]?\.'
# parte domínio (que deve ser um hostname válido)
domain_word = r'(%s)+\w{2,4}' % domain_label
# o conjunto
email_regex = '^%s@%s$' % (local_word, domain_word)
# flag CASEINSENSITIVE adicionada
email_c = re.compile(email_regex, re.I)
def valida_email(word):
'Devolve True se o email é válido. False caso contrário.'
if email_c.match(word) is not None:
parts = word.split('@')
local_part, domain_part = parts[0], parts[1]
if '..' in word or len(word)>256:
return False
return True
return False
Como expressões regulares não implementam memória, nesta implementação, não é possível validar o tamanho total do email, tampouco a existência de dois caracteres ponto adjacentes na parte local. Outro detalhe importante é que duas regras da especificação de email foram ignoradas nesta implementação. A primeira é que emails do tipo "Italo Maia"@dominio.com não são aceitos assim como emails do tipo local@IP_ADDRESS também não são aceitos. Isso foi feito de caso pensado. O primeiro formato não é aceito por conta desta RFC (ref:wikipedia), enquanto o segundo formato não é aceito por ser utilizado, principalmente, por spammers (ref:wikipedia).
Ufa! Passar essa validação para javascript é bem fácil. Vejamos:
// uma expressão regular assim deve funcionar:
/^[a-z0-9!#$%&'*+-/=?^_`{|}~][a-z0-9!#$%&'*+-/=?^_`{|}~.]{0,62}[a-z0-9!#$%&'*+-/=?^_`{|}~]?@([a-z0-9][a-z0-9\-]{0,61}[a-z0-9]?\.)+\w{2,4}$/
// agora basta fazer a verificação de tamanho
// total e de presença de '..' no email
Bem, é isso. Se alguém encontrar algum erro no meu código e puder me informar, eu agradeço.