Чтение, обработка непрерывного двоичного файла – Эффективно

У меня очень большие двоичные файлы без строки и без полевых разделителей. Цель состоит в том, чтобы эффективно обрабатывать эти файлы в файлах с разделителями табуляции.

Файлы структурированы следующим образом:

Каждая запись представляет собой фиксированную длину, 20 байтов. Каждое поле имеет разную длину, три поля длиной 3, 7 и 10 байт соответственно. Каждое поле также представляет другой тип данных, поля 1 и 2 – это int и 3 – char.

Каким будет наиболее эффективный метод обработки этих файлов? Я хотел бы сохранить его максимально простым, используя инструменты Bash, dd / od sed / awk, избегая perl / python, если это возможно, если разница в производительности не является экстремальной.

Ниже приведена рабочая попытка, это очень медленно. Я новичок в вышеупомянутых инструментах, поэтому подробные объяснения очень ценятся.

binfile="binfile.BIN" for (( i = 0 ; i <= 20000000 ; i += 20 )) do field1=$( od "${binfile}" -An --skip-bytes"$((${i}))" --read-bytes=3 --format=dI ) field2=$( od "${binfile}" -An --skip-bytes"$((${i}+3))" --read-bytes=7 --format=dI ) field3=$( od "${binfile}" -An --skip-bytes"$((${i}+10))" --read-bytes=10 --format=c ) echo - ${field1}'\t'${field2}'\t'${field3} >> output.tab done 

 fold -b -w 20 | cut --output-delimiter $'\t' -b 1-3,4-10,11-20 

Если ваш «разрез» не поддерживает -output-delimiter, попробуйте «gcut» (вырезание GNU) или подумайте об установке GNU coreutils.

(Пожалуйста, сообщите нам, как быстро различные решения, которые вы пытаетесь сделать 🙂

 open my $fh, '<:raw', shift; local $" = "\t"; while ( read $fh, my $rec, 20 ) { my @f = unpack 'a3 a7 a10', $rec; print "@f\n"; } 

Считывает из STDIN, выводит в STDOUT и выполняет проверку ошибок:

 #!/usr/bin/perl use strict; use warnings; use constant BLOCK_SIZE => 20; binmode STDIN; while (1) { my $rv = read(STDIN, my $buf, BLOCK_SIZE); die("Error: $!\n") if !defined($rv); last if !$rv; die("Error: Insufficient data\n") if $rv != BLOCK_SIZE; print(join("\t", unpack('a3 a7 a10', $buf)), "\n"); } 

Но я уверен, что вы найдете это медленнее, чем чтение намного больше за раз, поэтому я бы использовал следующее:

 #!/usr/bin/perl use strict; use warnings; use constant BLOCK_SIZE => 20; binmode STDIN; my $buf; while (1) { my $rv = sysread(STDIN, $buf, BLOCK_SIZE*64*1024, length($buf)); die("Error: $!\n") if !defined($rv); last if !$rv; while (length($buf) >= BLOCK_SIZE) { print(join("\t", unpack('a3 a7 a10', substr($buf, 0, BLOCK_SIZE, '')), "\n"); } } die("Error: Insufficient data\n") if length($buf);