web-dev-qa-db-ja.com

基数2の数値文字列の配列をバイナリファイルにRuby

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

動作するように見えますが、それを配列に読み込むことができません。

どのエンコーディングを使用すればよいですか?

19
Ivan Kozlov

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"

読み取り結果の優先形式がわかりません。また、上記の方法は非効率的です。しかし、とにかく、unpackの結果から「0」または「1」を順番に取得して、ハフマンツリーをトラバースできます。

28
M. Shiina

ビットが必要な場合は、手動でパックとアンパックの両方を行う必要があります。 Rubyも他の一般使用言語もあなたのためにそれをしません。

配列には文字のグループである文字列が含まれていますが、バイトの配列を作成し、それらのバイトをファイルに書き込む必要があります。

これから:_["010", "1111", "10", "10", "110", "1110", "001", "110", "000", "10", "011"]_

これらのバイトを構築する必要があります:_01011111 01011011 10001110 00010011_

それは4バイトなので、それらを単一の32ビット数値_01011111010110111000111000010011_、つまり_5F5B8E13_ hexに入れることができます。

コードのサンプルはどちらも異なることを行います。最初のファイルは、Ruby配列の文字列表現をファイルに書き込みます。2番目のファイルは32バイトを書き込みます。各バイトは_48_( '0')または_49_のいずれかです) ( '1')。

ビットが必要な場合、出力ファイルのサイズはちょうど4バイトにする必要があります。

それを達成する方法を学ぶためにビット操作について読んでください。


これがドラフトです。私はそれをテストしませんでした。何かが間違っている可能性があります。

_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で割り切れない場合に対応するために、7つのゼロが追加されています。これらのゼロがない場合、bit_sequence.scan(/.{8}/)は残りの文字をドロップします。

4
Sergey Bolgov