2015-11-20 3 views
3

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

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

Каждая запись имеет фиксированную длину, 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 
+1

и у вас нет/не разрешается писать 5 линии С-программу? Это было бы очень просто и очень-очень быстро. Удачи. – shellter

+0

Я не знаю о C, который требует кучу шаблона, прежде чем вы сможете даже открыть файл, но в значительной степени ничего, кроме оболочки, было бы замечательно. – Borodin

+0

Когда вы говорите двоично, вы имеете в виду, что первые 3 байта и следующие 7 байты представляют фактические целочисленные данные в их представлении на уровне машины, а не в ascii их версиях? И 3, и 7 - необычные длины для целочисленных данных (вместо этого ожидалось бы 4 и 8), поэтому должно быть некоторое дополнение. Кроме того, речь идет о достоверности данных. Есть ли вероятность, что вы можете опубликовать короткий шестнадцатеричный дамп некоторых выборочных данных и соответствующего вывода? – mhawke

ответ

0
open my $fh, '<:raw', shift; 

local $" = "\t"; 

while (read $fh, my $rec, 20) { 
    my @f = unpack 'a3 a7 a10', $rec; 
    print "@f\n"; 
} 
+0

Развертывает ли это первые два поля в своих целочисленных эквивалентах или просто выдает данные как есть? Не следует ли разделить результирующие поля на вкладку? – mhawke

+0

@ mhawke: Кажется, что вы не понимаете ни вопроса, ни моего ответа. Почему вы допрашиваете нас? – Borodin

+1

Гораздо менее магически использовать 'say join '\ t", unpack ... '. Почему бы вам не захотеть использовать '$"?? – ikegami

0

читает из 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); 
+0

Конечно, вам нужно иметь дело с EINTR, когда вы используете sysread. – chansen

+0

@chansen, Только если у вас есть обработчики сигналов. Но если это так, вам нужно обработать 'EINTR' для всех системных вызовов (' open', 'sysread',' print', 'open' и т. Д.), А не только' sysread'. – ikegami

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

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

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

Смежные вопросы