Perl Brasil

Pesquisar

Documentação

Artigos

Planeta

Eventos

Comunidade

r2 - 04 Apr 2007 - AlceuJunior

Parse de Endereços em Perl

A partir de uma discussão na cascavel-pm (http://mail.pm.org/pipermail/cascavel-pm/2007-March/009129.html), discutimos como fazer ETL de Endereços em uma base de dados não estruturada para uma base de dados com o endereço estruturado.

Definindo o problema

Para definir o problema, vamos definir um conjunto de test cases de formatos de endereço que vamos tentar fazer parse, como o TestDrivenDevelopment diz, escrever os testes é o seu planejamento.

use strict;
use warnings;

use Test::More qw(no_plan);

# Definindo o nome que o módulo deve ter:
# Brasil::Parse::Endereco, mas para fim de praticidade dos testes
# vamos chamar de BrasilParseEndereco para poder ter soh os dois
# arquivos no mesmo diretorio.

BEGIN { use_ok('BrasilParseEndereco') }
require_ok('BrasilParseEndereco');

# Nesses testes estão definidas a API, junto com os próprios testes.
# As implementações podem ser orientadas a objeto ou não, mas todas
# devem fornecer um método simples chamado
# "BrasilParseEndereco::parse($endereco)"
# que receba uma string e retorne uma lista com os seguintes elementos:
# ( $tipo_logradouro, $logradouro, $numero, $complemento )

# Por uma questão de praticidade, vou criar uma rotina que testa cada parte
# do endereco
sub is_endereco {
          my ($endereco, $exp_tipo_logradouro,
                $exp_logradouro, $exp_numero, $exp_complemento) = @_;
          my ($got_tipo_logradouro, $got_logradouro,
                $got_numero, $got_complemento) = BrasilParseEndereco::parse($endereco);
          is($got_tipo_logradouro, $exp_tipo_logradouro, 'Tipo Logradouro para: '.$endereco);
          is($got_logradouro, $exp_logradouro, 'Logradouro para: '.$endereco);
          is($got_numero, $exp_numero, 'Numero para: '.$endereco);
          is($got_complemento, $exp_complemento, 'Complemento para: '.$endereco);
}

# Agora temos os test cases


# Este é o tipo de endereço padrão. É o mais fácil de fazer parse
is_endereco('Rua Teste da Silva, 23 apto 3 bloco 4', 'Rua', 'Teste da Silva', '23', 'apto 3 bloco 4');

# Mais alguns casos:

# Acesso é um tipo de logradouro
# Esse é um caso muito confuso,
is_endereco('Acesso 27 14 1o andar', 'Acesso', '27', '14', '1o andar');
# Vamos dar uma maozinha para esse outro caso
is_endereco('Acesso 27, 14 1o andar', 'Acesso', '27', '14', '1o andar');

# Sempre normalizamos as formas de escrever o tipo de Logradouro
# Também normalizamos o "sem número" para 'S/N'
is_endereco('Av. Santa Bárbara do Oeste sem número', 'Avenida', 'Santa Bárbara do Oeste', 'S/N', '');

Implementações

Ifs encadeados, separando cada parte

Esta implementação foi a implementação que Daniel Ruoso fez há muito tempo atrás, quando precisou realizar ETL de endereço:

package BrasilParseEndereco;

use strict;
use warnings;

sub parse {
        my $endereco = shift;
        my $original = $endereco;
        my ($t,$l,$n,$c) = ('','','','');

        if ($endereco =~ /^rua\,*\s*(.+)$/i) {
                $t = "Rua";
                $endereco = $1;
        } elsif ($endereco =~ /^av\.*\,*\s*(.+)$/i) {
                $t = "Avenida";
                $endereco = $1;
        } elsif ($endereco =~ /^trav\.*\,*\s*(.+)$/i) {
                $t = "Travessa";
                $endereco = $1;
        }

        if ($endereco =~ /^([^,]+)\s*,\s*(.+)$/i) {
                $l = $1;
                $endereco = $2;
        } elsif ($endereco =~ /^(.+)\s*N\.*º\s*(.+)$/i) {
                $l = $1;
                $endereco = $2;
        } elsif ($endereco =~ /^(.\D+)\s+(\d+?.+)$/i) {
                $l = $1;
                $endereco = $2;
        }

        if ($endereco =~ /^(\S+)\s+(.+)$/) {
                $n = $1;
                $c = $2;
        } else {
                $n = $endereco;
                $c = '';
        }

        return ($t,$l,$n,$c);
}

1;