2016/04/02

JavaScriptでCSVファイルなどを文字化けさせずに出力する方法

JavaScriptでCSVやテキストファイルなどを出力したとき、環境や見るツールによっては文字化けしてしまう。たとえばCSVで出力して、テキストエディタでは読めるけど、Excelで開くと文字化けするなど。

結論からいうと、テキストの先頭にBOMをつければ解消される。
ということで、各ブラウザ(IE, Safari, Firefox, Chrome, Opera...)でファイルを出力するときに文字化けさせない方法をまとめる。



BOM(Byte Order Mark)とは


そもそもBOMとはなんぞや?

Unicodeの符号化形式で符号化したテキストの先頭につける数バイトのデータのことである。このデータを元にUnicodeで符号化されていることおよび符号化の種類の判別に使用する。

バイトオーダーマーク - Wikipedia

具体的に説明すると、Unicodeが登場したころ各国でいろんな文字コードが使われていた。日本ならSJIS、アメリカならASCIIといった具合に。

そこでUnicodeなのかSJIS,ASCIIなのかを明確に区別する必要があり、テキストの先頭に特定のコードを埋め込み区別できるようにしていた。そのコードがBOM。
ちなみにUTF-8の場合は「0xEF 0xBB 0xBF」という3バイトを先頭につける。



BOM付きファイルを出力する


var csv = 'ここにCSVのデータを入れる';

var link = document.createElement('a');
var bom = new Uint8Array([0xEF, 0xBB, 0xBF]);
var blob;

if (window.navigator.msSaveOrOpenBlob) {
  // for ie
  blob = new Blob([bom, csv], {type: 'text/csv'});
  window.navigator.msSaveOrOpenBlob(blob, csvName);
} else if (window.webkitURL && window.webkitURL.createObjectURL) {
  // for chrome (and safari)
  blob = new Blob([bom, csv], {type: 'text/csv'});
  link.setAttribute('download', csvName);
  link.setAttribute('href', window.webkitURL.createObjectURL(blob));
  link.click();
} else if (window.URL && window.URL.createObjectURL) {
  // for firefox
  blob = new Blob([bom, csv], {type: 'text/csv'});
  link.setAttribute('download', csvName);
  link.setAttribute('href', window.URL.createObjectURL(blob));
  link.click();
}

new Uint8Array([0xEF, 0xBB, 0xBF]) がUTF-8のBOMになる。
new Blob([bom, csv]) でBOM付きのファイルをつくる。

あとはブラウザによって出力方法を変えている。
ただし、Safariの場合はうまく動作しないので注意!

「IE○ね!!」ってより「Safari○ね!!」と思うことが多いのは、私だけだろうか?




より具体的な実装方法を知りたい方は、以下の記事を参照してください。

参考リンク





以上

written by @bc_rikko

2 件のコメント :

  1. > そこでUnicodeなのかSJIS,ASCIIなのかを明確に区別する必要があり、
    > テキストの先頭に特定のコードを埋め込み区別できるようにしていた。そのコードがBOM。

    大嘘書かないで下さい。BOMは文字通り「バイトオーダー」を示すマークです。
    Unicode(UTF-16)は1文字16ビットの文字体系ですが、16ビットワードは8ビットバイト2つ分の大きさがあります。
    そして2つの8ビットバイトを16ビットワードとして読み込む際にどちらが上位8ビットになるかはコンピュータのアーキテクチャに依存します。
    先行バイトが下位バイトになるリトルエンディアンなコンピュータで出力したファイルと、
    先行バイトが上位バイトになるビッグエンディアンなコンピュータで出力したファイルを区別するのがBOMです。
    BOMはBOMの上位下位を入れ替えた文字を使用しないという取り決めによってエンディアンを判別する為の文字です。

    なので8ビットバイトで定義されているUTF-8には本来不要な文字なのですが、他の文字コードではまず使わないバイトシーケンスが特徴的に付与されるため、文字コード判別のために敢えて付与する場合がある、というだけです。
    なおUTF-8のBOM付加はASCII系と完全な互換性があるはずのUTF-8を純粋なASCII系では処理できないファイルにしてしまうため、実用面でも賛否が分かれる使用法です。

    まぁなんにせよ文字コードの判別は後からできた応用であって、BOMの目的は全く異なります。
    大嘘書かないで下さい。

    返信削除
    返信
    1. ご指摘ありがとうございます。

      「文字コードの判別」は応用であって、本来の目的ではない。
      「Byte Order Mark(バイトの並び順の符号)」の名の通り、本来の目的は「符号化の種類(エンディアンの違い)を区別する」ため。

      ということですね。

      削除