LINUX.ORG.RU

Как одиним махом записать массив байтов в двоичный файл?

 ,


0

1

Привет, ЛОР.

До сих пор я на Перле только строки обрабатывал, теперь вот возникла необходимость попробовать себя в байтосношательстве, и вещи, которые на сишке делаются элементарно, здесь ставят в тупик.

Допустим я читаю с помощью read() двоичный файл в переменную $buf и пишу его в другой файл print-ом. Это работает, файлы идентичны.

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

Можно как-то поколдовать через pack/unpack? Или писать по одному байту?

★★★★★

print спокойно пишет бинарные данные, вот из моей же статьи про встроенный бинарник:

 my $bin = `echo "'"$bin_encoded"'" | base64 -d | gunzip`;
 print $fh $bin;

Так что задача сводится к итерации массиву и вызову print в цикле с преобразованием в бинарный вид.

Или вот выдержка из Perl Cookbook:

To convert a Perl integer to a text string of ones and zeros, first pack the integer into a number in network byte order[3] (the «N» format), then unpack it again bit by bit (the «B32» format).

sub dec2bin {
    my $str = unpack("B32", pack("N", shift));
    $str =~ s/^0+(?=\d)//;   # otherwise you'll get leading zeros
    return $str;
}

To convert a text string of ones and zeros to a Perl integer, first massage the string by padding it with the right number of zeros, then just reverse the previous procedure.

sub bin2dec {
    return unpack("N", pack("B32", substr("0" x 32 . shift, -32)));
}
alex0x08 ★★★
()
Последнее исправление: alex0x08 (всего исправлений: 1)
........
print {$fd} pack 'a4', 'QMAP';
print {$fd} pack 'N', 1;
print {$fd} pack 'N', scalar keys %keys;
print {$fd} pack 'N', 0;
for my $item (keys %keys) {
    print {$fd} pack 'nnNCCn', $item, $keys{$item}{unicode}, $keys{$item}{qt}, 0,   0, 0;
}
......
imb ★★
()

которые на сишке делаются элементарно

На сишке это делается вовсе не элементарно - если ты о возможности вызвать write на int[] или struct foo[] то нет, так нельзя, это не переносимо. Нужно приводить к типам фиксированной длины, явно управлять выравниванием и конвертить endianess. Может оказаться проще сериализовать в байты руками используя (для целых) сдвиги и битовые операции. В перле всё то же самое, этим занимается pack/unpack.

slovazap ★★★★★
()
Ответ на: комментарий от hobbit

Слава богу, C99 уже давно наступил и типы фиксированной длины есть в стандарте :)

Целочисленные типы фиксированной длины — только половина дела. Есть ещё big-endian и little-endian архитектуры, так что записанный через write(2) массив интов всё ещё не очень переносим.

debugger ★★★★★
()
Ответ на: комментарий от Norgat

Во-первых, #pragma не входит в стандарт и не обязана ничего делать. Во-вторых, о каком ты вопросе из трёх что я упомянул. В-третьих, даже если бы в языке была возможность определить структуру так чтобы та однозначно сериализовалась в поток байт/бит/октетов, от необходимости конвертить в неё рантайм структуру всё равно никуда не уйти.

slovazap ★★★★★
()
Ответ на: комментарий от hobbit

Аналогично ответу выше а) это только одна проблема из трёх что я упомянул и б) от необходимости конвертить между рантайм и сериализуемой структурами никуда не уйти.

slovazap ★★★★★
()