Rubyではバイナリデータを扱うメソッドとしてpackメソッドとunpackメソッドを利用できます。そしてpackメソッドで配列をバイナリデータに変換し、unpackメソッドでそれを配列に戻すの基本です。
そもそもバイナリデータがわからない、という方のために、unpackメソッドが対象とするバイナリデータと合わせてunpackメソッドの使い方を紹介します。
packとunpackの関係
Rubyのプログラムでは、配列の内容をバイナリデータに変換するpackメソッドと、そのバイナリデータを配列に戻すunackメソッドを利用できます。そして今回紹介するunpackメソッドはバイナリデータを対象とします。
unpackメソッドの使い方を紹介する前にバイナリデータについて解説します。
そもそもバイナリデータとは
コンピュータが扱うファイルには、テキストエディタ等で編集できるテキストデータとアプリケーションが扱う内部データをそのまま出力したバイナリデータの2種類があります。
テキストデータは全て文字コードで記載されているので、人が読めるデータです。しかし記載されたデータに応じて文字列や数字に変換しなければなりません。
一方バイナリデータはプログラムが扱う変数の値がそのまま保存されているので、そのままでは人が読めません。しかしプログラムで読み込んだらそのまま変数として処理に利用できるので便利です。そのためパソコン用のアプリケーションでは、昔からデータの保存にバイナリデータを利用しています。
Rubyでバイナリデータを書き込むには
バイナリデータとしてわかりやすいのは数字を保存する方法です。プログラムで作成した数字をファイルに保存する際、テキストデータで書き出すよりもバイナリデータで書き出した方が少ないサイズで済みます。
ただし数字をバイナリデータで書き出す際、数字を何ビットで表現するかを決めなければなりません。具体的にはバイナリデータの最小単位は8bitで、256までの数字を表現できます。もっと大きな数字を扱う場合は最小単位4つ分の32bitや8つ分の64bitを検討することになります。
そしてRubyで数字をバイナリデータに変換する際に使われるのがpackメソッドです。packメソッドでは数字を何ビットで表現するかをテンプレートを指定し、数字をバイナリデータに変換します。
バイナリデータから数字に戻すのがunpack
先ほど紹介したRubyのpackメソッドで数字を変換したバイナリデータは、Rubyのunpackメソッドで数字に戻せます。そしてpackメソッドで利用したテンプレートと同じものをunpackでも利用します。
なおpackメソッドが対象にするのは配列なのに対し、unpackメソッドが対象にするのは文字列です。unpackメソッドは文字列のバイナリデータを対象とし、packメソッドが対象とすした配列と同じものを返すます。
unpackメソッドの文法
unpackメソッドは、packメソッドで作成したバイナリーデータの文字列を対象に、引数にpackメソッドで使ったのと同じテンプレート文字列を指定することで、対象としたバイナリデータを元の配列と同じ配列を返すメソッドです。
unpackメソッドの文法
packメソッドで作成した文字列.unpack(テンプレート文字列)
unpackメソッドの使用例
arr = [1,2,3] bincode = arr.pack("I*") p bincode # "\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00"が表示される out = bincode.unpack("I*") p out # [1, 2, 3]が表示される
unpackメソッドで使えるテンプレートとは
Rubyのpackで配列からバイナリーデータを作成し、unpackでバイナリーデータから配列に戻す場合、テンプレート文字列を指定します。そしてテンプレート文字列の組み合わせにより、いろいろな変換が可能です。
なおテンプレート文字列で使える機能は数が多く、今では使われないフォーマットへの変換用もあります。次から代表的なテンプレート文字列の使い方を紹介します。
固定長の文字列を扱う
文字列をバイナリデータに変換した場合、文字コードがそのまま使われます。しかし、バイナリデータでは文字列を格納する長さが決められており、文字列を格納して余った領域には文字が無いことを表すヌル文字で埋められます。そのためテキストエディタでは編集できません。
packで固定長の文字列に変換するにはテンプレート文字「a」と文字数を使い、それをunpackで配列に戻すのにテンプレート文字「A」と文字数を使います。
固定長の文字列を扱う例
arr = ["12345"] bincode = arr.pack("a10") p bincode # "12345\x00\x00\x00\x00\x00"が表示される out = bincode.unpack("A10") p out # ["12345"]が表示される
数字を扱う
数字をバイナリデータに変換して扱う場合、先ほど紹介したように数字に合わせたビット数を考慮したテンプレート文字を使います。
例えば整数を32bitで扱う場合、0から4,294,967,296まで扱えます。そして32bitの数字をバイナリーデータに変換するテンプレート文字が「I」です。先ほどunpackメソッドの使用例で紹介した例が数字を32bitで扱っています。
32bitで数字をバイナリデータに変換する例
arr = [1,2,3] bincode = arr.pack("I*") p bincode # "\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00"が表示される out = bincode.unpack("I*") p out # [1, 2, 3]が表示される
浮動小数点を扱う
浮動小数点をバイナリデータに変換して扱うことも可能です。そして倍精度浮動小数点数として扱うには、テンプレート文字「d」を使います。
倍精度浮動小数点数を扱う例
arr = [3.14] bincode = arr.pack("E") p bincode # "\x1F\x85\xEBQ\xB8\x1E\t@"が表示される out = bincode.unpack("E") p out # [3.14]が表示される
その他のデータ
Rubyのpackとunpackでは、今の時代に使われなくなった古いフォーマットもサポートしています。例えばメールに添付したファイルはbase64でフォーマットが使われましたが、packとunpackはbase64に対応しています。またUNIX間のシリアル通信に使われていたuuencodeも扱えます。
さらにバイナリーデータを16進数や2進数に変換することも可能です。もし、そのようなデータを扱う場合は、Rubyの公式マニュアルのテンプレート文字の説明などを参考にしてください。
unpack1メソッドの使い方
unpackメソッドで配列に戻したら要素が1つだけというケースがあります。そのようなケースではRubyのunpack1メソッドを利用することで、その1だけの要素を取り出すことが可能です。
次からunpack1メソッドの使い方について紹介します。
unpack1メソッドの文法
unpack1メソッドは、packメソッドで作られたバイナリーデータを対象にし、展開されてた配列の最初の値を返すメソッドです。そして引数にunpackメソッドと同じく、packメソッドで使われたのと同じテンプレート文字列を指定します。
unpack1メソッドの文法
packメソッドで作成した文字列.unpack1(テンプレート文字列)
unpackメソッドの使用例
arr = [1,2,3] bincode = arr.pack("I*") p bincode # "\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00"が表示される out = bincode.unpack1("I*") p out # 1 が表示される
これはは、[1, 2, 3]の3つの要素からなる配列をバイナリーデータに変換し、unpack1メソッドで最初の要素である1のみを取り出した例です。
要素が1つのみデータの例
unpack1メソッドが便利な点は、要素が1しかない配列をpackメソッドで扱うケースです。先ほど紹介した固定長の文字列を扱う例にunpack1メソッドの適用したケースを紹介します。
固定長の文字列を扱う例(unpack1を適用)
arr = ["12345"] bincode = arr.pack("a10") p bincode # "12345\x00\x00\x00\x00\x00"が表示される out = bincode.unpack1("A10") p out # "12345"が表示される
unpackメソッドを使うと要素が1つだけの配列になっていましたが、unpack1メソッドを使うことで文字列として受け取ることが可能です。
まとめ
これまで紹介したようにRubyのunpackメソッドは、packメソッドでバイナリーデータに変換したデータを対象に、packメソッドで使ったテンプレート文字列を使って配列に戻すメソッドです。packメソッドとセットで利用するか、他のシステムで作成されたバイナリデータから必要な情報を取り出す目的で利用されます。
なおunpackメソッドの代わりにunpack1メソッドを利用することで、最初の要素を取り出せます。1つの要素のみ扱う場合は、unpack1メソッドの利用も検討してください。