Validando endereço de e-mail com máquina de estado

pic2 No post sobre máquina de estado eu falei que iria fazer um exemplo mais completo para melhor entendimento dos conceitos.

Neste post vou abordar em como construir uma função que valide a construção de um endereço de e-mail utilizando o conceito de máquina de estado.

Uma das formas de se validar uma sequência de caracteres é utilizando expressão regular, mas nem todos ambientes temos expressões regulares para validação. Por exemplo temos o Delphi, apesar de ser possível adicionar esta funcionalidade.

Uma curiosidade, internamente, a rotina que processo uma expressão regular, possuí uma máquina de estado para processar a expressão passada. Inclusive em ambientes como .NET, a expressão regular é “compilada” gerando código CIL (Common Intermediate Language), um algorítimo na forma de máquina de estado.

O que faremos aqui usando diagramas e máquina de estado, nada mais é o que o algorítimo de expressão regular faz automaticamente.

Premissas para o endereço de e-mail: Para o nosso exemplo adoto as seguintes regras, deve existir apenas uma arroba (@). Deve existir pelo menos 1 caracter antes do arroba, este caracter deve ser de “A” à “Z”, de “a” à “z”, de “0″ à “9″, “-” (hífen), “_” (travessão) ou “.” (ponto). Depois do arroba teriamos uma sequência de pelo menos 2 caracteres válidos, um ponto e pelo menos 2 carcteres válidos, sendo que pode-se repetir o ponto mais caracteres válidos.

Dado estas premissas, eu montei a seguinte expressão regular:

[A-Za-z0-9-_.]+@[A-Za-z0-9-_]{2,}(\.[A-Za-z0-9-_]{2,})+

Para testar, entre neste site e copie e cole a expressão acima.

Por esta expressão regular, o endereço a@bb.cc é considerado válido, mas um @bb.cc não é. Faça testes com vários endereços de e-mail e verifique se todas as premissas foram atendidas.

Agora vou montar um diagrama de máquina de estado que seguirá as premissas passadas:

email_estados

Para montar este diagrama, foi feito estado por estado (começando do “1″) e tentando seguir as premissas. Para exemplificar: no estado 1 eu vejo a premissa de que é necessário pelo menos 1 caracter válido antes do arroba. E no estado 2 verifico uma seqüência de n caracteres válidos (observe a transição do estado 2 para o próprio estado 2).

Sendo que [A-Za-z0-9-_.]+ é o mesmo que [A-Za-z0-9-_.][A-Za-z0-9-_.]*. Podemos dizer que [A-Za-z0-9-_.] é representado pelo estado 1 e [A-Za-z0-9-_.]* é representado pelo estado 2.

A mesma regra vale para achar os outros estados e transições. Para fixar a idéia, sugiro fazer um funcionamento da máquina de estado mentalmente como dissemos neste post.

Observe que em qualquer estado, se as condições não são válidas, saímos imediatamente. A unica forma de considerar um e-mail válido é percorrer todos os estados e no oitavo satisfazer a condição FIM que executa a ação OK!!!.

Na seqüência mostro a função em Pascal que implementa o diagrama acima:

function IsEMailValido(const AEMail: string): Boolean;
const
  V = [‘a’..‘z’, ‘A’..‘Z’, ‘0′..‘9′, ‘_’, ‘-’];
  VPonto = V + [‘.’];
var
  email: string;

  function Proximo: Char;
  begin
    if email <> then
    begin
      Result := email[1];
      Delete(email, 1, 1);
    end
    else
      Result := #0;
  end;

  function Estado1(ALetra: Char; var Valido: Boolean): Integer;
  begin
    if ALetra in VPonto then
      Result := 2
    else
      Result := 9;
  end;

  function Estado2(ALetra: Char; var Valido: Boolean): Integer;
  begin
    if ALetra in VPonto then
      Result := 2
    else if ALetra = ‘@’ then
      Result := 3
    else
      Result := 9;
  end;

  function Estado3(ALetra: Char; var Valido: Boolean): Integer;
  begin
    if ALetra in V then
      Result := 4
    else
      Result := 9;
  end;

  function Estado4(ALetra: Char; var Valido: Boolean): Integer;
  begin
    if ALetra in V then
      Result := 5
    else
      Result := 9;
  end;

  function Estado5(ALetra: Char; var Valido: Boolean): Integer;
  begin
    if ALetra in V then
      Result := 5
    else if ALetra = ‘.’ then
      Result := 6
    else
      Result := 9;
  end;

  function Estado6(ALetra: Char; var Valido: Boolean): Integer;
  begin
    if ALetra in V then
      Result := 7
    else
      Result := 9;
  end;

  function Estado7(ALetra: Char; var Valido: Boolean): Integer;
  begin
    if ALetra in V then
      Result := 8
    else
      Result := 9;
  end;

  function Estado8(ALetra: Char; var Valido: Boolean): Integer;
  begin
    if ALetra in V then
      Result := 8
    else if ALetra = ‘.’ then
      Result := 6
    else if ALetra = #0 then
    begin
      Valido := True;
      Result := 9;
    end
    else
      Result := 9;
  end;

type
  TTestaEstado = function(ALetra: Char; var Valido: Boolean): Integer;
var
  TestaEstado: array [1..8] of TTestaEstado;
  estado: Integer;
begin
  TestaEstado[1] := @Estado1;
  TestaEstado[2] := @Estado2;
  TestaEstado[3] := @Estado3;
  TestaEstado[4] := @Estado4;
  TestaEstado[5] := @Estado5;
  TestaEstado[6] := @Estado6;
  TestaEstado[7] := @Estado7;
  TestaEstado[8] := @Estado8;
  email := AEMail;
  Result := False;
  estado := 1;
  while estado < 9 do
    estado := TestaEstado[estado](Proximo, Result);
end;
 

O código é apenas uma materialização do diagrama, nenhuma otimização foi feita.

2 Respostas para “Validando endereço de e-mail com máquina de estado”


  1. 1 Douglas Cunha

    Muito bom. Outra coisa legal da máquina de estado é que fica fácil inserir novos estados a qualquer altura, sem precisar mudar muita coisa.

  2. 2 Alecão

    Concordo com você. Alterar o diagrama e trazer isso para o código torna-se trivial. Facilitando a manutenção de código.

Deixe uma Resposta