2013-05-29 2 views
19

Я написал простую кодировку Хаффмана в 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 

, который выглядит, как это работает, но тогда я не могу прочитать его в массив.

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

+0

Что вы хотите, чтобы ваш файл содержал? Персонажи? Или биты? Или массив Ruby? Или что-то другое? –

+0

Биты. Coz Мне нужно меньше размера файла. –

+0

Ответ, вероятно, будет включать использование 'pack' –

ответ

27

Я думаю, вы можете просто использовать 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, чтобы пересечь дерево Хаффмана.

+0

@IvanKozlov Здесь вы можете увидеть, как читать бит обратно в строку. Теперь вам просто нужно разбить его на куски, используя декодирование Хаффмана. –

3

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

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

Отсюда: ["010", "1111", "10", "10", "110", "1110", "001", "110", "000", "10", "011"]

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

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

Оба образца вашего кода делают разные вещи. Первый записывает в файл строковое представление массива 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}/) упадет остальные символы.

+0

Не могли бы вы вставить пример кода?Может ли массив, как упаковать и распаковать его? –

+0

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

+0

Я добавил проект как пример. У меня не было времени тщательно его проверить. –

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