Подтвердить что ты не робот

Как читать/записывать двоичные файлы?

Я написал простую кодировку Хаффмана в Ruby. В качестве вывода у меня есть массив, например:

["010", "1111", "10", "10", "110", "1110", "001", "110", "000", "10", "011"]

Мне нужно написать, а затем прочитать, это в файл и из него. Я пробовал несколько методов:

IO.binwrite("out.cake", array)

Я получаю простой текстовый файл, а не двоичный.

Или:

File.open("out.cake", 'wb' ) do |output|
  array.each do | byte |
       output.print byte.chr
  end
end

Кажется, что это работает, но потом я не могу прочитать его в массиве.

Какую кодировку я должен использовать?

4b9b3361

Ответ 1

Я думаю, вы можете просто использовать Array#pack и String#unpack как следующий код:

# Writing
a = ["010", "1111", "10", "10", "110", "1110", "001", "110", "000", "10", "011"]
File.open("out.cake", 'wb' ) do |output|
  output.write [a.join].pack("B*")
end

# Reading
s = File.binread("out.cake")
bits = s.unpack("B*")[0] # "01011111010110111000111000010011"

Я не знаю вашего предпочтительного формата для результата чтения, и я знаю, что вышеупомянутый метод неэффективен. Но в любом случае вы можете взять "0" или "1" последовательно из результата unpack, чтобы пересечь ваше дерево Хаффмана.

Ответ 2

Если вам нужны биты, вам придется делать как упаковку, так и распаковку вручную. Ни Ruby, ни какой-либо другой язык общего пользования не сделает это за вас.

В вашем массиве содержатся строки, которые представляют собой группы символов, но вам нужно построить массив байтов и записать эти байты в файл.

Из этого: ["010", "1111", "10", "10", "110", "1110", "001", "110", "000", "10", "011"]

вы должны построить эти байты: 01011111 01011011 10001110 00010011

Поскольку это всего лишь четыре байта, вы можете поместить их в одно 32-разрядное число 01011111010110111000111000010011, которое 5F5B8E13 hex.

Оба образца вашего кода делают разные вещи. Первый записывает в файл строковое представление массива Ruby. Второй записывает 32 байта, каждый из которых имеет значение 48 ('0') или 49 ('1').

Если вам нужны биты, размер выходного файла должен быть всего четыре байта.

Прочитайте о битовых операциях, чтобы узнать, как добиться этого.


Вот черновик. Я не проверял. Что-то может быть не так.

a = ["010", "1111", "10", "10", "110", "1110", "001", "110", "000", "10", "011"]

# Join all the characters together. Add 7 zeros to the end.
bit_sequence = a.join + "0" * 7  # "010111110101101110001110000100110000000"

# Split into 8-digit chunks.
chunks = bit_sequence.scan(/.{8}/)  # ["01011111", "01011011", "10001110", "00010011"]

# Convert every chunk into character with the corresponding code.
bytes = chunks.map { |chunk| chunk.to_i(2).chr }  # ["_", "[", "\x8E", "\x13"]

File.open("my_huffman.bin", 'wb' ) do |output|
  bytes.each { |b| output.write b }
end

Примечание: в регистр обрабатываются семь нулей, когда общее количество символов не делится на 8. Без этих нулей bit_sequence.scan(/.{8}/) отбрасывает оставшиеся символы.