今回は「バイナリ (binary)」というデータ型について説明します。Erlang のバイナリは、バイナリデータ (binary data) を扱うためのデータ構造です。基本的には 1 byte (要素の値は 0 から 255 まで) の 1 次元配列ですが、ビット単位でも操作できるようになっています。
バイナリは次の式で生成することができます。
<< 値:サイズ/タイプ, ... >>
値はデフォルトで整数、サイズはビット幅を指定します。整数以外の値を使用する場合はタイプを指定します。サイズの指定を省略した場合は 8 bit (1 byte) になります。
簡単な例を示します。
> <<1, 2, 3, 4>>. <<1,2,3,4>> > <<16#0102:16, 5, 6>>. <<1,2,5,6>> > <<1:4, 2:4, 3, 4>>. <<18,3,4>>
最初の例は要素が 1, 2, 3, 4 のバイナリになります。次の例は、最初の要素が 16 bit の値 16#0102 なので、上位 8 bit と下位 8 bit に分けて格納され、バイナリの値は <<1,2,5,6>> になります。最後の例は、2 つの要素が 4 bit なので、1 つのバイトデータ 18 にまとめられて、大きさが 3 のバイナリデータになります。
バイナリの要素は文字列または文字でも指定することができます。簡単な例を示します。
> <<"abc">>. <<"abc">> > <<$a, $b, $c>>. <<"abc">>
バイナリの大きさは組み込み関数 (BIF) の size/1 で求めることができます。
> size(<<1, 2, 3, 4>>). 4 > size(<<1, 2, 3, 4, 5, 6, 7, 8>>). 8
タイプで指定できる属性の種類を下記に示します。複数の属性を指定するときはハイフン (-) で区切ります。
簡単な例を示しましょう。
> <<1.2345/float>>. <<63,243,192,131,18,110,151,141>> > <<123, 5.6789/float, <<1, 2, 3, 4>>/binary>>. <<123,64,22,183,49,143,197,4,129,1,2,3,4>> > <<16#01020304:32>>. <<1,2,3,4>> > <<16#01020304:4/unit:8>>. <<1,2,3,4>> > <<16#01020304:4/native-unit:8>>. <<4,3,2,1>> > <<16#01020304:4/little-unit:8>>. <<4,3,2,1>> > <<16#01020304:4/big-unit:8>>. <<1,2,3,4>>
UTF-8 が扱える端末であれば日本語を表示することもできますが、バイナリで日本語を使用するときはタイプに utf8 を指定する必要があります。werl +pc uncode での実行例を示します。
> "あいうえお". "あいうえお" > io:format('~p~n', ["あいうえお"]). [12354,12356,12358,12360,12362] ok > <<"あいうえお">>. <<"BDFHJ">> > <<"あいうえお"/utf8>>. <<"あいうえお"/utf8>>
format で日本語 (utf8) を表示するときは ~s, ~p の代わりに ~ts, ~tp を使います。
> io:format('~ts~n', ["あいうえお"]). あいうえお ok > io:format('~tp~n', ["あいうえお"]). "あいうえお" > A = <<"あいうえお"/utf8>>. <<"あいうえお"/utf8>> > io:format('~p~n', [A]). <<227,129,130,227,129,132,227,129,134,227,129,136,227,129,138>> ok > io:format('~tp~n', [A]). <<"あいうえお"/utf8>> ok
バイナリは 1 byte (8 bit) が標準の単位で、ビットの総数は 8 の倍数になりますが、それ以外のデータ (ビットの総数が 8 の倍数にはならないデータ) でも取り扱うことができます。Erlang ではこれを「ビットストリング (bitstring)」といいます。基本的な操作はバイナリと同じですが、パターンマッチングで動作が異なる場合があります。
簡単な例を示します。
> <<1:2>>. <<1:2>> > <<1,2,3,4:4>>. <<1,2,3,4:4>> > <<1:4,2,3,4>>. <<16,32,48,4:4>>
最初の例は 2 bit のビットストリングになります。次の例は、最後のデータが 4 bit なので、28 bit のビットストリングになります。最後の例は、最初のデータが 4 bit なので、2 番目以降のデータが 4 bit ずつ左へシフトされて <<16,32,48,4:4>> となります。
なお、関数 size/1 にビットストリングを適用すると、バイト単位での大きさを返します。ビット単位で大きさを求める場合は組み込み関数 bit_size/1 を使ってください。
> size(<<1:4>>). 0 > size(<<1,2,3,4:4>>). 3 > bit_size(<<1:4>>). 4 > bit_size(<<1,2,3,4:4>>). 28 12> bit_size(<<1,2,3,4>>). 32
パターンマッチングはバイナリでも行うことができます。次の例を見てください。
> <<A, B, C, D>> = <<1, 2, 3, 4>>. <<1,2,3,4>> > A. 1 > B. 2 > C. 3 > D. 4 > <<X:4,Y:4>> = <<129>>. <<129>> > X. 8 > Y. 1 > A1 = <<255, 1.2345/float, <<1, 2, 3, 4>>/binary>>. <<255,63,243,192,131,18,110,151,141,1,2,3,4>> > <<X1, Y1/float, Z1/binary>> = A1. <<255,63,243,192,131,18,110,151,141,1,2,3,4>> > X1. 255 > Y1. 1.2345 > Z1. <<1,2,3,4>>
バイナリで整数以外のデータとパターンマッチングするときは、/ の後ろにタイプを指定してください。これでバイナリの要素が float や binary でもパターンマッチングで取り出すことができます。また、ビット幅を指定して値を取り出すこともできます。
リストのパターンマッチングのように、先頭の要素と残りのデータとに分けてマッチングさせることも可能です。次の例を見てください。
> <<H, R/binary>> = <<1, 2, 3, 4, 5>>. <<1,2,3,4,5>> > H. 1 > R. <<2,3,4,5>>
先頭のデータをバイト単位ではなくビット単位で取り出すことも可能です。この場合、残りのデータはビットストリングになるので、タイプには bitstring を指定します。また、ビットストリングからバイトデータを取り出す場合も、残りのデータがビットストリングになるので bitstring を指定します。簡単な実行例を示します。
> <<Hb:4, Rb/bitstring>> = <<16, 2, 3, 4, 5>>. <<16,2,3,4,5>> > Hb. 1 > Rb. <<0,32,48,64,5:4>> > <<Hb1, Rb1/bitstring>> = <<1, 2, 3, 4, 5:4>>. <<1,2,3,4,5:4>> > Hb1. 1 > Rb1. <<2,3,4,5:4>>
この場合、Rb と Rb1 に binary を指定するとマッチングでエラーになります。
ここで Erlang に用意されているビット演算子について説明しておきましょう。
演算子 | 機能 |
---|---|
band | ビットごとの論理積を返す |
bor | ビットごとの論理和を返す |
bxor | ビットごとの排他的論理和を返す |
bnot | ビットごとの論理的な否定を返す |
bsl | m bsl n は m を n ビットだけ左シフトする |
bsr | m bsr n は m を n ビットだけ右シフトする |
band はビットごとの論理積を返します。
> 5 band 3. 1
0101 and 0011 --------- 0001
bor はビットごとの論理和を返します。
> 5 bor 3. 7
0101 or 0011 -------- 0111
bxor はビットごとの排他的論理和を返します。
> 5 bxor 3. 6
0101 xor 0011 --------- 0110
bnot はビットごとの論理的な否定を返します。
> bnot 0. -1 > bnot 1. -2
m bsl n は m を n ビット左シフトします。m bsr n は m を n ビット右シフトします。
> 1 bsl 8. 256 > 256 bsr 4. 16
Erlang はリストだけではなくバイナリにも「内包表記」を使うことができます。基本的な構文を次に示します。
<< <<式1>> || <<パターン>> <= 式2, 条件式 >>
<< >> の最初にバイナリの要素を生成する式1 (コンストラクタ) を記述します。次に || で区切ったあと、"<<パターン>> <= 式2" を記述します。式2の値はバイナリでなければなりません。式1とパターンは << >> で囲ってください。式2はバイナリをそのまま指定してもかまいません。<= はバイナリの先頭から順番に要素を取り出し、パターンとマッチングを行います。また、カンマで区切ったあと、条件式を指定することができます。この条件式はガードとは違って、真偽値を返す関数であれば何でもかまいません。
簡単な例を示しましょう。
> << <<(X * 2)>> || <<X>> <= <<1, 2, 3, 4>> >>. <<2,4,6,8>> > << <<X>> || <<X:4>> <= <<1, 2, 3, 4>> >>. <<0,1,0,2,0,3,0,4>> > << <<Y:4, X:4>> || <<X:4, Y:4>> <= <<1, 2, 3, 4>> >>. <<16,32,48,64>>
最初の例はパターンが <<X>> なので、バイナリから 8 bit ずつデータを取り出して変数 X にセットします。次に X * 2 を計算して、その値がバイナリの要素になります。2 番目の例は、パターンが <<X:4>> なので、バイナリから 4 bit ずつデータを取り出します。その値がそのままバイナリの要素になるので、生成されるバイナリの大きさは 8 byte になります。最後の例は、上位 4 bit と下位 4 bit を反転させたバイナリを生成します。
パターンで指定したビット幅が式で指定したビット幅よりも大きい場合、上位のビットが捨てられて下位のビットが有効になる事に注意してください。次の例を見てください。
> << <<X>> || <<X:16>> <= <<1, 2, 3, 4, 5, 6, 7, 8>> >>. <<2,4,6,8>>
パターンは <<X:16>> なので、バイナリから 16 bit ずつデータを取り出します。式は <<X>> なので、生成する要素は 8 bit になります。この場合、上位 8 bit は捨てられて、値は下位 8 bit になります。したがって、生成されるバイナリは <<2,4,6,8>> になるわけです。
バイナリの内包表記で、"<<パターン>> <= 式" のかわりにリスト内包表記の "パターン <- 式" を使うことができます。逆に、リスト内包表記で "<<パターン>> <= 式" を使うこともできます。これにより、バイナリからリストへの変換、およびその逆変換を行うことができます。
簡単な例を示します。
> << <<X>> || X <- [1, 2, 3, 4, 5] >>. <<1,2,3,4,5>> > [X || <<X>> <= <<1, 2, 3, 4, 5>>]. [1,2,3,4,5] > << <<X>> || X <- [1, 2, 1000, 4, 5] >>. <<1,2,232,4,5>> > << <<X:16>> || X <- [1, 2, 1000, 4, 5] >>. <<0,1,0,2,3,232,0,4,0,5>> > << <<X>> || X <- [1, 2, a, 4, 5] >>. ** exception error: bad argument
リストからバイナリに変換する場合、リストの要素の値に注意してください。値がビット幅に収まらない場合、余分な上位ビットは捨てられて下位ビットがバイナリの要素になります。また、数値以外のデータを与えるとエラーになります。
なお、Erlang の組み込み関数 (BIF) にはリストをバイナリに変換する関数 list_to_binary と、バイナリをリストに変換する関数 binary_to_list が用意されています。簡単な実行例を示します。
> list_to_binary([1, 2, 3, 4, 5]). <<1,2,3,4,5>> > binary_to_list(<<1, 2, 3, 4, 5>>). [1,2,3,4,5]
このほかに、モジュール binary にはバイナリを操作するための便利な関数が用意されています。また、モジュール erlang にもデータをバイナリに変換する関数やその逆変換を行う関数があります。詳細は Erlang のリファレンスマニュアル Erlang -- binary, Erlang -- erlang をお読みください。