Edited at

javascriptでバイナリを扱うために

More than 5 years have passed since last update.

html5の時代になってjsでバイナリデータを扱うことが増えてきている.

そんななかで覚えておくべきことをいくつかメモしておく.

C言語におけるintやshortなどの型ついて知っておくと楽になる.

具体的なことはあまり書いてないので実際にそういったプログラムを作るには,

出てきた単語をぐぐって欲しい.


そもそもバイナリデータをどうやって扱う?

jsでは主にUint8Arrayという配列を使って扱うことが多いだろう.

このUint8Arrayというのは数値しか記録できず,値が0から255までの初期化時に長さが決まる配列である.

Uint8Arrayというのはつまり,負の数なし(Unsigned)の整数(int)で,要素一つあたり1バイト(8bit = 1byte)の配列(Array)ということである.

C言語におけるunsigned char[]に相当する.

この配列は連想配列ではなく純粋な配列であることに注意する.

バイナリデータとjsを結びつけるためにはFileAPIやXMLHttpRequestを用いる.


Uint8ArrayとArrayBuffer

バイナリデータの扱い方について調べるとよく出てくるものにArrayBufferというものがある.

ArrayBufferは実際にメモリ上に確保されているバッファを示すものである.

しかし,直接値を読んだり書き込んだりすることができないためそのまま使用することはほとんどない.

基本的にはUint8Arrayの引数に渡してUint8Arrayのインスタンスを作るなどに使われる.

Uint8Arrayは直接読み書き可能なので扱いやすい.

ArrayBufferからデータを読み出すだけでいいというのならDataViewというものを使うことで簡単に読み出すことができる.

ただしDataViewを使用する場合はエンディアンに注意する.

追記:

DataViewについて詳しく調べてみるとどうやら書き込みもできてエンディアンの指定もできるようだ.

DataViewが使えるなら積極的に使った方が良さそうだ.

ただしDataViewには文字を読み込む機能がないので,文字の読み込みは文字コードに注意しながら自分で書く必要がある.

またごくまれにだがビット単位でバイナリを扱わなければならないこともある.

そういう場合はDataViewが使えないので,そんな場合の例を下の方に追記しておく.


TypedArray

C言語のunsigned short[]float[]にあたるUint16ArrayFloat32Arrayなどの配列も用意されている.

これらの配列をTypedArrayと呼び,全て共通して型付きで純粋な配列である.

バイナリデータからshortfloatで値を取り出したいときに使用する.

自分でエンディアンを指定することができずCPUネイティブで処理される(多くの場合はリトルエンディアン).

また,アライメントの問題なども出てくるため少し使いづらい.


stringは2バイト

jsのstringは基本的に1文字に付き2バイトであるためUint8Arrayに格納するときは注意が必要である.

文字コードにも注意する.


Uint8Arrayから値を読み出す

DataViewを使わないととたんにめんどくさくなる.

使える場合はどんどん使えばよいがそうでない場合はどうすればいいのか.

Uint8Arrayからビッグエンディアンのshort(負数あり2バイト)のデータを読み出すサンプルを示す.


readBigendianShort.js

var uint8array = new Uint8Array(arrayBuffer);  //arrayBufferにはデータが入っているとする

var value = (uint8array[0] << 8) + uint8array[1];
if(value & 0x8000){
value = -((value - 1) ^ 0xffff); //2の補数計算
}
alert(value); //valueに変換された値

ビックエンディアンや2の補数については調べればたくさん出てくるので割愛する.

追記:バイナリをビット単位で扱わないといけない例

swfファイルのヘッダにはFrameSizeというフィールドがある.

このフィールドのデータを得るためにはビット単位でバイナリを読み込んでいく必要がある.

(参考URL)SWFバイナリ編集のススメ第一回

このFrameSizeを読み込むコードの例を以下に示す.


readRect.js

function readRect(arraybuffer){

var uint8array = new Uint8Array(arraybuffer);
var NBits = uint8array[0] >> 3;
var frameSize = {NBits:NBits};
var filedNames = ['Xmin', 'Xmax', 'Ymin', 'Ymax'];
var bitOffset = 2; //最上位bitのオフセットを7とする
var byteOffset = 0;
for(var i = 0;i < 4;i++){
var temp = 0;
for(var j = 0;j < NBits;j++){
temp <<= 1;
var mask = 1 << bitOffset;
temp += (uint8array[byteOffset] & mask) >> bitOffset;
bitOffset--;
if(bitOffset < 0){
byteOffset++;
bitOffset = 7;
}
}
frameSize[filedNames[i]] = temp;
}
return frameSize;
}

このreadRect関数をreadRect([0x70, 0x00, 0x09, 0x60, 0x00, 0x00, 0x96, 0x00])のような感じで呼び出してやるとFrameSizeがかえってくる.

Uint8Arrayは普通のArrayを引数に渡しても大丈夫なのでこれも扱いやすいポイントのひとつである.

上記の例では{NBits: 14, Xmin: 0, Xmax: 4800, Ymin: 0, Ymax: 4800}のようなオブジェクトがかえってくる.


最後に

バイナリをいじくるプログラムはjsでは書きやすいとは言えないと思います.

ただ,一度書けば基本的にWindowsでもMacでもiPhoneでも動くし,ブラウザ上で動くのですごく楽しい.

クライアントサイドjsのみでzipを作成したり,音楽ファイルをエンコードしたりするライブラリが既に出てきていて,昔では考えられないような状態になってきました.

このビックウェーブに乗り遅れないよう楽しいバイナリライフを送りましょう!