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;
