我想读取一个大的二进制文件(500MB)并获取位于每5000字节重复一次的标头之后的特定字节。为此,我有一个简短的片段,以二进制模式读取文件,块大小为16536。
代码按预期工作,但会耗尽所有可用内存,使其无法使用。我尝试关闭并打开我每次写入操作时写入的解析输出文件,但这没有用。问题可能与我阅读二进制文件的方式有关吗?
这是我的代码:
use strict;
my $BLOCK_SIZE=16536;
my $fname = $ARGV[0];
my $fparsename = $ARGV[1];
open(F,"<$fname") or die("Unable to open file $fname, $!");
binmode(F);
my $buf;
my $ct=0;
my $byte=0;
my $byte_old=0;
my $byte_cnt=0;
my $byte_lock=0;
my $sample_msb=0;
my $sample_lsb=0;
my $sample_16b=0;
my $out_form='';
open(my $fh, '>', $fparsename) or die "Could not open file '$fparsename' $!";
print $fh ("Sample, Value \n");
close($fh);
while(read(F,$buf,$BLOCK_SIZE,$ct*$BLOCK_SIZE)){
foreach(split(//, $buf)){
$byte_old = $byte;
$byte = ord($_); # fetch byte (in decimal)
if (($byte_old == 202) && ($byte == 254)) { # CA = 202, FE = 254
$byte_cnt = 0;
$byte_lock = 1;
}
if ($byte_lock == 1) {
$byte_cnt++;
}
if ($byte_cnt == 20) { # 20th byte after CAFE in header
$sample_msb = $byte;
}
if ($byte_cnt == 21) { # 21th byte after CAFE in header
$sample_lsb = $byte;
}
if (($byte_cnt == 21) && ($byte_lock == 1)) { # lock down and concatenate
$byte_lock = 0;
$byte_cnt = 0;
$sample_16b = sprintf("%X", $sample_msb) . sprintf("%X", $sample_lsb);
$out_form = sprintf("%d, %s \n", $ct++, $sample_16b);
open(my $fh, '>>', $fparsename) or die "Could not open file '$fparsename' $!";
printf $fh $out_form;
close($fh);
}
}
$ct++;
}
close(F);
close($fh);
答案 0 :(得分:4)
嗯,首先,吃内存的东西是记忆中的东西。关闭和打开文件句柄没有任何区别,因为将根据需要刷新写入。
我无法重现您的问题,因为我没有合适的示例文件。
但是我觉得你的问题是你的read
错了:
可以指定OFFSET将读取数据放在字符串中除开头之外的某个位置。负OFFSET指定从字符串末尾向后计数的多个字符的位置。大于SCALAR长度的正OFFSET会导致字符串在附加读取结果之前用“\ 0”字节填充到所需大小。
所以...从文件中读取'last',将创建一个长度为500MB的字符串,大部分为空字节。 (出于各种原因,它可能会超过500MB的实际内存)
只需省略read
中的偏移量即可解决此问题。
但失败了:
所以我会仔细研究你的变量 - 特别是你将它们全部放在循环之外,并且意味着它们可能被连接,而不是重写。
我还建议,如果您按字节迭代文件,可以通过设置$/
来大大简化。 E.g。
local $/ = \16536
while ( my $buf = <$input> ) {
}
来自perlvar
将$ /设置为对整数的引用,包含整数的标量或可转换为整数的标量将尝试读取记录而不是行,最大记录大小为引用的整数字符数。
(或者如评论中所述 - 假设你的'块'是5000字节,那么设置local $/ = \5000
可能是明智的。
我还建议 - 使用3 arg open,而不是2 arg:
open ( my $input, '<:raw', $fname ) or die $!;