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[]
にあたるUint16Array
やFloat32Array
などの配列も用意されている.
これらの配列をTypedArray
と呼び,全て共通して型付きで純粋な配列である.
バイナリデータからshort
やfloat
で値を取り出したいときに使用する.
自分でエンディアンを指定することができずCPUネイティブで処理される(多くの場合はリトルエンディアン).
また,アライメントの問題なども出てくるため少し使いづらい.
stringは2バイト
jsのstring
は基本的に1文字に付き2バイトであるためUint8Array
に格納するときは注意が必要である.
文字コードにも注意する.
Uint8Arrayから値を読み出す
DataView
を使わないととたんにめんどくさくなる.
使える場合はどんどん使えばよいがそうでない場合はどうすればいいのか.
Uint8Array
からビッグエンディアンのshort
(負数あり2バイト)のデータを読み出すサンプルを示す.
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を読み込むコードの例を以下に示す.
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を作成したり,音楽ファイルをエンコードしたりするライブラリが既に出てきていて,昔では考えられないような状態になってきました.
このビックウェーブに乗り遅れないよう楽しいバイナリライフを送りましょう!