Hatena::Groupjavascript

JavaScriptで遊ぶよ

 | 

2011-02-26

XMLHttpRequest と UTF-16

16:24

XMLHttpRequest でバイナリを扱う伝統的作法である x-user-defined について書いたことがある。

x-user-defined だと、バイナリの1バイトが UTF-16 の1文字 (2バイト) に変換されることになるので、大きなファイルを扱うにはあまりよろしくないかもしれない。

じゃあ最初から charset=utf-16 とかでダウンロードしちゃえばいいんじゃないの?と思って色々試してみた。


実験

UTF-16 なテキストファイル (Big/Little Endian, BOM あり/なし) と、バイナリっぽいファイルを overrideMimeType を使ってダウンロードしてみて、それぞれどういうふうに JavaScriptString に変換されているかを見る。

試した charset は x-user-defined, utf-16, utf-16le, utf-16be の4つ。utf-16le とかあったの知らなかった。

もしバイト数が奇数だったら最後のバイトがどうなるかも見る。

Windows の Firefox 3.6, Chrome 9, Opera 11.01 で試した。IE9 RC は overrideMimeType が無かった。


結果

  • Chrome では charset=x-user-defined だと、BOM つきのファイルは UTF-16 として解釈された。今回一番びっくり。
  • Firefox と Opera では charset=utf-16 だと、Endian がどちらであろうが、BOM がついていようがいまいが、guess してくれた。(ASCII の範囲だったからかもしれない)
  • Chrome では charset=utf-16 だと、BOM のない Big Endian が Little Endian として解釈された。
  • charset=utf-16le だと、Big Endian の方は BOM が取られたり取られなかったり変なバイトが入ってしまったり色々おかしい。逆も然り。
  • charset=utf-16, utf-16le, utf-16be だと、奇数バイト数のファイルの最後のバイトは黙殺される。

結論

  • UTF-16 のファイルは BOM を付けるか、charset で utf-16le, utf-16be の正しいほうを指定する。
  • バイナリファイルは x-user-defined のほうが無難。どうしても utf-16 にしたいときは、サーバー側で偶数バイトにして、utf-16le か utf-16be のどちらかを指定する。
  • x-user-defined では BOM つきのファイルを扱わない。

余談

XMLHttpRequest Level 2 では、 xhr.responseType = 'blob' とか xhr.responseType = 'arraybuffer' やってリクエストすると、xhr.response でそれぞれ FileAPI の BlobWebGL の ArrayBuffer が取れることになっている。

Blob だと FileReader を使って readAsArrayBuffer とかやったら ArrayBuffer が得られる。他にも readAsBinaryString とか readAsDataURL とかがある。

ArrayBuffer で受け取った場合は、var ui8ary = new Uint8Array(arraybuffer); alert(ui8ary[0]); とかできる。

↑は自分もよくわかってないので間違ってるかもしれないし、これから仕様が変わるかもしれない。


やっぱり UTF-16 は使ってはいけない

不完全なサロゲートペアの片割れだけで出てきた場合、Firefox私用領域replacement characterにマッピングされてしまうらしい。例えば 0xA1 0xDE を utf-16le で読むと U+DEA1 ではなくて U+FFFD になってしまうなど。

上のテストにも追加しておいた。

結論。x-user-defined を使いましょう。

えむけいえむけい2011/04/09 22:41U+FFFDは私用領域じゃありませんよ。replacement characterです。

edvakfedvakf2011/04/10 10:42ありがとうございます。Unicode 的下駄文字ですね。知ってたはずですが replacement character を私用領域に脳内変換してたようです。

トラックバック - http://javascript.g.hatena.ne.jp/edvakf/20110226
 |