我有一个二进制文件,其中一堆数据块一个接一个地存储。数据块的格式如下:
Length [byte] Content Description
2 0xFFFF Data block header
4 Epoch time seconds since 00:00:00 UTC, January 1, 1970
2 value of N Length of data following this value
N Data Data itself
我尝试使用unpack,但这是错误的,因为数据的长度不固定。
我需要编写一个子程序,它将读取和解析数据块(每次调用子程序时一个数据块),直到文件结束。
该文件正在使用big-endian。
这是我到现在为止所尝试的:
use strict;
use warnings;
my $filename;
if (! $ARGV[0])
{
die "Input filename is required";
}
sub setFile
{
$filename = $_[0];
}
my $inFile = $ARGV[0];
setFile($inFile);
open INFILE, $filename or die "\nUnable to open input file";
binmode INFILE;
my $nbytes;
while (<INFILE>) {
my( $header, $timestamp_hex, $datalength_hex ) = unpack 'H4 H8 H4', $_;
my $timestamp = hex($timestamp_hex);
my $datalength = hex($datalength_hex);
print "$timestamp $datalength\n";
for (my $i = 0; $i < $datalength; $i++)
{
my $data = unpack 'H', $_;
print "$data";
}
print "\n";
}
close INFILE
or die "Error while closing $filename: $!\n";
答案 0 :(得分:2)
<INFILE>
毫无意义。它会读取,直到找到换行符。
如果您将整个文件存储在内存中,则可以使用以下命令:
my @fields = unpack('( n N n/a* )*', $file);
while (@fields) {
my ($sig, $ts, $data) = splice(@fields, 0, 3);
die "Incorrect signature" if $sig != 0xFFFF;
process_rec($ts, $data);
}
如果我们要从数据中单独提取标题,我们可以节省内存并添加一些错误检查。
use constant HEADER_FORMAT => 'nNn';
use constant HEADER_LENGTH => length(pack(HEADER_FORMAT, 0, 0, 0));
while (length($file)) {
last if length($file) < HEADER_LENGTH;
my ($sig, $ts, $data_len) = unpack(HEADER_FORMAT, substr($file, 0, HEADER_LENGTH, ''));
die "Incorrect signature" if $sig != 0xFFFF;
last if length($file) < $data_len;
process_rec($ts, substr($file, 0, $data_len, ''));
}
die "Premature EOF" if length($file);
从文件句柄中读取是第二个片段的扩展。如果您没有将整个文件存储在内存中,则可以使用以下命令:
use constant HEADER_FORMAT => 'nNn';
use constant HEADER_LENGTH => length(pack(HEADER_FORMAT, 0, 0, 0));
use constant BLOCK_SIZE => 128*1024;
sub make_fill_to = sub {
my $fh = shift;
my $buf_ref = \shift;
my $eof = 0;
return sub {
my $bytes_needed = $_[1];
while (!$eof && length($$buf_ref) < $bytes_needed) {
my $rv = sysread($fh, $$buf_ref, BLOCK_SIZE, length($$buf_ref));
die $! if !defined($rv);
$eof = 1 if !$rv;
}
return !$eof;
}
};
my $buf = '';
my $fill_to = make_fill_to($fh, $buf);
while (1) {
$fill_to->(HEADER_LENGTH)
or last LOOP;
my ($sig, $ts, $data_len) = unpack(HEADER_FORMAT, substr($buf, 0, HEADER_LENGTH, ''));
die "Incorrect signature" if $sig != 0xFFFF;
$fill_to->($data_len)
or last LOOP;
process_rec($ts, substr($buf, 0, $data_len, ''));
}
die "Premature EOF" if length($buf);
当使用select
来管理多个句柄时,必须先读取,所以我习惯以这种方式编写它。以下是如果重构将读取放在首位,上面会是什么样子:
use constant HEADER_FORMAT => 'nNn';
use constant HEADER_LENGTH => length(pack(HEADER_FORMAT, 0, 0, 0));
use constant BLOCK_SIZE => 128*1024;
my $buf = '';
my ($got_header, $sig, $ts, $data_len);
while (1) {
my $rv = sysread($fh, $buf, BLOCK_SIZE, length($buf));
die $! if !defined($rv);
last if !$rv;
while (1) {
if (!$got_header) {
last if length($buf) < HEADER_LENGTH;
($sig, $ts, $data_len) = unpack(HEADER_FORMAT, substr($buf, 0, HEADER_LENGTH, ''));
die "Incorrect signature" if $sig != 0xFFFF;
$got_header = 1;
}
last if length($buf) < $data_len;
process_rec($ts, substr($buf, 0, $data_len, ''));
$got_header = 0;
}
}
die "Premature EOF" if $got_buffer || length($buf);