2017-02-15 21 views
0

大きなバイナリファイル(500MB)を読み込み、5000バイトごとに繰り返されるヘッダの後にある特定のバイトをフェッチします。そのため、バイナリモードで16536のブロックサイズで短いスニペットを読み込んでいます。perlでバイナリファイルを読む - メモリ不足

コードは意図したとおりに動作しますが、使用できないメモリをすべて使い果たしてしまいます。私は閉じて、書き込み操作を行う必要があるたびに私が書き込む解析された出力ファイルを開くことを試みたが、それは役に立たない。この問題は、私がバイナリファイルを読んでいる方法にリンクできますか?メモリは、メモリに保持されているものです食べる事 - 最初のオフ、

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); 
+1

ではなく小数を使用して、それらに 'CA = 202のコメントを、FEは= 254'は' 0xCA'を使用する必要がありますし、 '0xFE'。 – Borodin

答えて

4

まあ:

は、ここに私のコードです。書き込みが必要に応じてフラッシュされるため、ファイルハンドルを閉じて開いても差はありません。

適切なサンプルファイルがないため、問題を再現できません。

Anが先頭以外の文字列でのいくつかの場所で読み出したデータを配置するように指定することができOFFSET:

は、しかし、私はあなたの問題は、あなたの read間違っを持っていることだと思います。負のOFFSETは、文字列の最後から数えて数えている文字の配置を指定します。 SCALARの長さよりも大きい正のOFFSETは、読み込みの結果が追加される前に、文字列が "\ 0"バイトで必要なサイズに埋められます。

だからファイルから読み込んだ文字列の長さは、ほとんどがnullバイトです。 (それはおそらく様々な理由で実際のメモリが500MB以上になるでしょう)

これは、単にreadのオフセットを省略することで簡単に修正できます。

しかし、それを失敗:

だから私はあなたの変数をよく見ます - 特に、あなたはループの外にそれらすべてをスコープ、そして彼ら書き換えではなく、連結されているかもしれない手段です。

ファイルをバイト単位で繰り返している場合は、$/を設定することで大幅に簡略化できます。例えば。整数への参照に$ /を設定perlvar

から

local $/ = \16536 

while (my $buf = <$input>) { 

} 

、整数に変換可能だ整数、またはスカラーを含むスカラは、最大レコードではなく、行のレコードを読み取るしようとしますsizeは、参照される整数の文字数です。

(または、「チャンク」が5000バイトであるとしたら、local $/ = \5000を設定することはおそらく賢明です)。

そして私もお勧めしたい - オープン3引数ではなく、2引数を使用します。

open (my $input, '<:raw', $fname) or die $!; 
+0

'\ 16536'を' $/'として選んだ理由を説明してください。 – simbabque

+1

「特定のバイト」が各チャンクに一度現れるように 'local $/= \ 5000 'を設定します。 – Borodin

+0

@simbabque - OPが読み込みサイズとして設定されているためです。 Borodinは、OPがユースケースを使用していることを考えると、5000がおそらくより賢明であると述べているからです。 – Sobrique