背景
Rubyでバイナリ列をパースするにはString#unpackメソッドを使う。
この際には、変数の型(32bit符号付き整数、倍精度浮動小数点数など)と、エンディアンを指定する。
例えば、ファイルからビッグエンディアンの倍精度浮動小数点数を読み込みたい場合、以下のようにする。
io = File.open(ARGV[0], 'rb')
b = io.read(8) # 8 byte読む
p b.unpack("G") # バイト列を 64bit double big-endian として解釈する
上の例の "G" というのがpackテンプレート文字列と呼ばれるもので、エンディアンと型を指定するもの。
パックテンプレート文字列の一覧
上記のページを見てもらうとわかるように、ビッグエンディアンの64bit符号付き整数については方法が提供されていない。(16,32bitについては方法が提供されている。)
そのため、64bit整数については自前で実装する必要がある。
(2022/8/12 追記)コメントで方法を教えていただきました。(@Nabetaniさんありがとうございます)
以下のようにすることでビッグエンディアンを指定できます。
b.unpack("q>")
公式のドキュメントにも
「強制的にエンディアンを指定したいときは、 リトルエンディアンなら< ビッグエンディアンなら> を後ろにつけます」と書いてありました。
以下の自前で実装する方法は不要になりました。
(追記おわり)
64bit signed integerに対して自前で実装する方法
以下のようなメソッドを用意する。
def read_signed64int(b)
big_endian = [1].pack("s") == [1].pack("n")
# s: system-dependent int16_t, n: big-endian 16bit integer
big_endian ? b.unpack("q") : b.reverse.unpack("q")
# q: long long (符号付き整数, エンディアンと long long のサイズに依存)
end
[1].pack("s") == [1].pack("n")
で実行しているシステムのエンディアンを確認。
システムがリトルエンディアンの場合は、バイト列をreverse
でリトルエンディアンに変更してから unpack("q")
でlong longとして解釈する。