Zenn
Open4

Rubyでバイト列を得る

shuichishuichi

前提

バイト列の並び(生データ)はStringクラス

Rubyでは、バイト列の並びもStringで表す。

https://docs.ruby-lang.org/ja/latest/class/String.html

バイト列を表す文字列

文字列ではない単なるバイト列も String オブジェクトで表されます。その時のエンコーディングはASCII-8BIT です。

バイト列の確認方法

実際に、バイト列がどんな様子になっているかを確認したいときは、String.bytesを使うとよい。

"hello".bytes #=> [104, 101, 108, 108, 111]

https://docs.ruby-lang.org/ja/latest/class/String.html

Kernel#putsで確認する場合には、引数に直接生データのStringを渡さないように、注意が必要(後述)。
一言でいうと、出力の際に生データ(バイト列)を文字の並びにエンコードしてしまい、その結果を見ることになるため。

一番混乱が少ないのは、ファイルに吐き出して、そのファイルの中身をhexdump -C <ファイル>などで見ることだが...

shuichishuichi

方法

Stringリテラル\xXXをつかう

るりまのリテラルのところにない?

Array<Integer>#packを使う

Integer9737の並びを、バイト列String\x61\x21にする:

bytes_str = [97, 37].pack("CC") # "C2", "C*"でもよい
puts bytes_str
#=> a%

puts bytes_str == "\x61\x21"
#=> true

https://docs.ruby-lang.org/ja/latest/method/Array/i/pack.html

Integer#to_s(16)は違うよ

Kernel.putsの標準出力などで動作確認をしていると勘違いしやすいけれど...
Integer#to_s(16)で得られるのは、「そのIntegerを16進表示した場合の 数字の文字 の並び」であって、実際のバイト列ではない。

具体的には、以下の例を見たほうがわかりやすいと思う:

puts 0.to_s(16) #=> "0"(『ゼロ』の一文字)
puts 0.to_s(16).bytes #=> 48

つまり、0.to_s(16)は、 バイト列的には0x30(10進数で48) になっている。(0x00ではない!)
ASCIIコード表を見ると、確かに「0」の 文字 はバイト列0x30に対応していることがわかる。

https://docs.ruby-lang.org/ja/latest/method/Integer/i/to_s.html
https://www.k-cube.co.jp/wakaba/server/ascii_code.html

shuichishuichi

Kernel#puts使用時の注意:直接バイト列Stringを引数に渡すと、内容確認はできないよ

バイト列Stringに対してKernel#putsを使っても、各バイトを対応する文字に直した状態の出力を見せられるだけ。

bytes_str_1 = [97, 37].pack("CC")
puts bytes_str_1
#=> a%

bytes_str_2 = [0, 18].pack("CC")
puts bytes_str_2
#=> □□(対応する文字がないのでTofuになる)

色々試しているとこんがらがるかもしれないが、「Stringの 『生データ』自体は常にバイト列 で、putsなどで標準出力する際に、対応する文字にエンコードしている」ということを考えれば、当然である。

つまり、「Kernel#putsによって何を見せられているのか」を把握しておくことが大事。

ファイルの中身に吐いたデータをテキストエディタで見ると、文字になっているのも、ファイル中の生データをエディタの画面上に出力する際に、対応する文字にエンコードしているだけ。

shuichishuichi

これをどっかにおく

[97, 99].pack("CC") == "ac"
#=> true

"\x61\x63" == "ac"
#=> true

"\x00" == [0].pack("C")
#=> true
ログインするとコメントできます