[Cascavel-pm] Qual a melhor estrutura pra lidar com o header de um arquivo de dados?
Rodrigo Torres
motoster em gmail.com
Segunda Agosto 15 14:23:10 PDT 2005
Olá, monges.
É um prazer participar desta lista.
Estou escrevendo, em perl, um aplicativo pra extrair (dump) informações
contidas no header de um arquivo binário de dados.
O header é mais ou menos assim:
00000000 4D 5A DD DD 04 08 0C 10 31 32 35 30 MZ......1250
0000000C 35 32 30 30 35 30 38 31 39 30 32 30 520050819020
00000018 30 30 30 32 30 30 30 32 36 36 39 37 000200026697
00000024 31 4E 4D 41 36 39 30 50 52 45 53 49 1NMA690PRESI
00000030 44 45 4E 54 45 20 4D 90 44 49 43 45 DENTE M.DICE
0000003C 20 20 20 20 20 20 20 20 20 20 20 20
00000048 20 50 52 45 46 45 49 54 55 52 41 20 PREFEITURA
00000054 4D 55 4E 49 43 49 50 41 4C 20 44 45 MUNICIPAL DE
00000060 20 50 52 45 53 49 44 45 4E 54 45 20 PRESIDENTE
0000006C 4D 90 44 49 43 45 20 20 20 20 20 20 M.DICE
00000078 20 20 20 20 20 20 20 20 20 20 20 20
00000084 20 20 20 20 20 20 20 20 20 20 20 20
00000090 20 20 20 20 20 20 20 20 20 20 20 20
0000009C 20 20 20 20 20 20 20 20 20 20 20 20
000000A8 20 20 20 20 20 20 20 20 20 20 20 20
000000B4 20 20 20 20 20 20 20 20 20 20 20 20
000000C0 20 20 20 20 20 20 20 20 20 20 20 20
000000CC 20 20 20 20 20 20 20 20 20 20 20 20
000000D8 20 20 20 20 20 20 20 20 20 20 20 20
000000E4 20 20 20 20 20 20 20 20 20 20 20 20
000000F0 20 20 20 20 20 20 20 20 20 30 30 30 000
000000FC 30 30 30 30 30 33 36 33 30 31 32 30 000003630120
00000108 30 35 30 30 31 31 39 33 00 00 00 00 05001193....
Como vocês já devem ter percebido, os campos desse header são strings
codificadas em IBM850.
0x0 até 0x3 é número mágico;
0x4 até 0x7 é reservado, e
o resto é informação em strings de tamanho fixo.
Eu sou novato em Perl, na verdade, sou novato em programação -- e já que
estou sendo sincero mesmo, sou novato em Linux também, hehe ;) , dei uma
pesquisada por aí mas não consegui encontrar nada muito direto ou
específico.
Dei uma olhada no módulo Image::Exiftool, que é uma ferramenta para
extrair informações de EXIF de dentro de um arquivo de imagem, mas
aquilo é puro exoterismo pra mim. Do pouco que entendi, vi que ele tem
uma problema um pouco diferente do meu, uma vez que os campos, em sua
maioria, têm seus valores dentro de uma lista de pré-definidos, o que
não é meu caso, já que lido com datas, horas e strings arbitrárias.
Enfim, depois de matutar um pouquinho (Impaciência), ponderar num modo
elegante de usar as informações desse header (Presunção) e mudar a
estrutura sem quebrar demais o programa (Preguiça :) cheguei ao seguinte
resultado:
my %Header = (
magic => '4d5a' . 'dddd', # expressão regular
tamanho => 512,
arquivo => undef,
campos => [
{ 'formato' => "a", 'valor' => undef, 'descricao' => "Versão do formato" },
{
'formato' => "a8",
'descricao' => "Data do backup",
'valor' => undef;
'funcao' => \&data
},
{
'formato' => "a6",
'descricao' => "Hora do backup",
'valor' => undef;
'funcao' => \&hora
},
{ 'formato' => "a5", 'descricao' => "Serial da instalação" },
{
'formato' => "a9",
'descricao' => "Tamanho do arquivo",
'valor' => undef;
'funcao' => \&tamanho
},
{ 'formato' => "a", 'valor' => undef, 'descricao' => "Envio Orçamento" },
{ 'formato' => "a2", 'valor' => undef, 'descricao' => "UF" },
{ 'formato' => "a3", 'valor' => undef, 'descricao' => "Código da Empresa" },
{ 'formato' => "a30", 'valor' => undef, 'descricao' => "Cidade" },
{ 'formato' => "a50", 'valor' => undef, 'descricao' => "Empresa" },
{ 'formato' => "a126", 'valor' => undef, 'descricao' => "Mensagem" },
{ 'formato' => "a5", 'valor' => undef, 'descricao' => "Último xxpess->cdpess" },
{ 'formato' => "a6", 'valor' => undef, 'descricao' => "Último xxitem->cdprod" },
{ 'formato' => "a2", 'valor' => undef, 'descricao' => "Quantidade de exercícios" },
{ 'formato' => "a4", 'valor' => undef, 'descricao' => "Exercício 1" },
{ 'formato' => "a6", 'valor' => undef, 'descricao' => "emmext->seqve_ls 1" },
)
## A chave magic é uma expressão regular porque esse formato vai mudar
no futuro e eu posso checar ele com
my $length = length $Header{magic};
read( $fh , my $magic, $length );
my $numbers = unpack( "H$length", $magic );
print "Wow! Got another\n" if ( $numbers !~ /$Header{magic}/ )
## A chave tamanho serve pra eu saber quantos bytes ler [1];
## A chave arquivo é pra eu por o nome do arquivo que está sendo
processado no momento.
## A chave campos é um array (porque a ordem importa) contendo hashes,
cujas chaves são explicadas a seguir:
* formato : Formato que vai ser concatenado pra resultar no
template do unpack como a seguir:
my $Template = join " ", map $_->{formato}, @{ $Header{campos} };
* descrição: A descrição do campo. Pensei nisso como uma forma de
auto-documentação que ajuda muito na hora de gerar o relatório
[2] ( Preguiça );
* valor : O que vai ser efetivamente extraído do header, e
* funcao : uma função[3] a ser chamada para formatar o campo
para impressão, de forma que eu simplesmente faço
foreach my $campo ( @{ $Header{campos} } ) {
if ( defined $$campo{funcao} ) {
$$campo{valor} = &{ $$campo{funcao} }( $$campo{valor} );
}
}
antes de imprimir.
Pois vejam bem, monges, que eu já pensei em usar um array simplesmente e
decorar a posição de cada campo. Isso me pareceu rápido, mas confuso.
Gostaria, no entanto pedir uma ajuda de vocês na forma de uma pergunta:
Eu estou fazendo alguma coisa realmente idiota, poderia estar fazendo
muito melhor, ou já tá bom? Gostaria de sugestões. Se alguém quiser ver
o código todo e um arquivo de exemplo, entre em contato.
Humildemente, seu menor aprendiz,
motobói
1 -
$bytes_read = read( $fh, my $dados, $Header{tamanho} )
or ++$error;
#VEJA A EXPLICAÇÃO SOBRE AS CHAVES PARA ENTENDER $Template
@fields = map decode( "ibm850", $_ ), unpack( $Template, $dados );
foreach ( my $i = 0 ; $i <= $#fields ; $i++ ) {
$Header{campos}[$i]{valor} = $fields[$i];
}
2 -
format =
@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<: ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$campo, $valor
~~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$valor
.
print ">>> $Header{arquivo}\n";
foreach ( @{ $Header{campos} } ) {
( $campo, $valor ) = @{$_}{qw/descricao valor/}; #hash slices
write();
}
3 - EXEMPLO DE UMA FUNÇÃO DE FORMATAÇÃO:
sub tamanho {
#Formata o campo tamanho do arquivo.
$_ = shift;
return sprintf "%d Kbytes ( %d bytes)", $_ / 1024, $_;
}
Mais detalhes sobre a lista de discussão Cascavel-pm