JavaScriptでSJISのCSVファイルを作成する

文字化けには注意

この記事を三行にまとめると

文字コードのところがちょいと面倒
変換用のライブラリを用意してくれている方がいる
たまに「PHPなら簡単にできるのに……」みたいなことができない
PHPやPythonでウェブサービスを運用していると時々CSVファイルを作成する場面に出くわしますが、同じ要領でJavaScriptでCSVファイルを作成してダウンロードするにはどうすれば良いのかってのが今回の話です。

JavaScriptでやる場合も決して難しくはないんですが、文字コードのところがちょいと面倒なんで、今日はその辺も含めて見ていきたいと思います。



とりあえず作成してみる

まずは文字コードとか気にしないでシンプルにボタンを押したらCSVが作成されるという処理を書いてみます。

//HTML
<button id="makecsv">CSV作成</button>

//JavaScript
$button = document.getElementById('makecsv');
$button.onclick = function() {
  text = 'あいうえお,かきくけこ,さしすせそ\nたちつてと,なにぬねの,はひふへほ';
  data = new Blob([text], {type: 'text/csv'});
  download = URL.createObjectURL(data);

  link = document.createElement('a');
  link.href = download;
  link.download = 'sample.csv';
  link.click();
  URL.revokeObjectURL(download);  
}

まず「new Blob」でBlogオブジェクト(バイナリデータのオブジェクト)を作成し、「URL.createObjectURL」でそのデータを使ったダウンロード用のURLを作成しています。どうやらBlobには配列を渡してやる必要があるみたいなので、上記でも[text]でデータを渡しています。

URLを作成したらそのURLを持つリンクを作成して強制的にクリックイベントを発生させています。ようは画像のdataURIとかと同じでCSVのBlobデータを持つURLを開くことができればそのCSVデータをファイルとしてダウンロードできるわけなので、擬似的にaタグを作ってそいつをクリックさせようという発想です。

最後の「URL.revokeObjectURL」はcreateObjectURLで作成したURLを解放する処理です。別にこの一行がなくてもCSVダウンロードは可能なんですが、メモリを解放するのとかと一緒でたぶんやっといた方が良い処理なんでしょう(あまりよく分かってない)

何はともあれこれでCSV作成ボタンを押したらCSVファイルがダウンロードできるようになります。CSVの各項目はコンマと改行コードで区切ればOKです。列を区切る時はコンマ、行を区切る時は改行コードを挿入します。つまり上記の場合だとCSVの中身はこんな感じの二行二列の状態になるってことですね。

あいうえお かきくけこ さしすせそ
たちつてと なにぬねの はひふへほ



SHIFT-JISに変換したい場合

おそらく今時のほとんどのウェブページは文字コードがUTF-8とかだと思うので、このままCSVファイルをダウンロードするとファイルの文字コードもUTF-8です。でもEXCELなどでファイルを開く時は文字コードがSHIFT-JISの方がありがたい場合もあると思います。

PHPとかだとわりと簡単に文字コードが変換できるんですが、JavaScriptで同じことをやろうとすると自分で文字コードの変換用のテーブルを用意しないといけないみたいで、ぶっちゃけかなりめんどくさいです。

そこで今回はすでに誰かが用意してくれた変換用のライブラリを使わせてもらって、SHIFT-JISに変換したCSVファイルをダウンロードできるようにしてみます。

今回使うのは下記のencoding.jsというライブラリです。
https://github.com/polygonplanet/encoding.js

//HTML
<button id="makecsv">CSV作成</button>
<script src="encoding.js"></script>

//JavaScript
$button = document.getElementById('makecsv');
$button.onclick = function() {
  text = 'あいうえお,かきくけこ,さしすせそ\nたちつてと,なにぬねの,はひふへほ';
  str = Encoding.stringToCode(text);
  convert = Encoding.convert(str, 'sjis', 'unicode');
  u8a = new Uint8Array(convert);
  
  data = new Blob([u8a], {type: 'text/csv'});
  download = URL.createObjectURL(data);

  link = document.createElement('a');
  link.href = download;
  link.download = 'sample.csv';
  link.click();
  URL.revokeObjectURL(download);  
}

SHIF-JISに変換する処理を追加しました。「Encoding.stringToCode」と「Encoding.convert」が文字コードを変換する処理です。この処理のためにencoding.jsを使用しています。

「new Uint8Array」は型付きの配列を作成する処理です。正直難しいことはよく分からんのですが、文字コードを変換しただけの配列(上記でいうとconvert変数の中身)をそんままBlobに渡すと変換テーブルの値がそのままCSVに出力されてしまいます。実際ログとかに出してみると分かるのですが、Uint8Arrayを通さずにCSVを作成するとファイルの中身が数字の羅列になっているはずです。なのでそれを避けるために型付きの配列に変換する処理を挟んでいます。

これで文字コードをSHIFT-JISに変換した状態のCSVファイルを作成できます。





JavaScriptってたまに「PHPなら簡単にできるのに……」みたいなことができなかったりしますよね。0埋めとか。PHPだとsprintfで簡単に実装できるんですけど、JavaScriptにはPHPのsprintfに相当するものがないみたいで、これも自作するか誰かが作ってくれたライブラリに頼る必要がある。まあ0埋めくらいならたいしたことないんですけど、個人的にはsprintfってわりと重宝しているので、それに相当するものがJavaScriptにも標準であると嬉しいなあと思ったことは何度かあります。

まあsprintfにしろ今回の文字コードの変換にしろ、「できたら良いのに」って考える人はやはり結構いるみたいなので、たいていどこかの誰かがそれを可能にするライブラリを作ってくださっていますから、いざこういう状況にぶち当たってもそこまで悲観することはないですけどね。本当にこういうライブラリを作ってくれる人には頭が上がらないですね。感謝感謝です。

PHPとかで開発してるとCSV作成はPHPでやれば良いや〜ってなると思うんでJavaScriptでCSVを作成しなきゃいけない場面ってそんなに多くないかもしれませんが、例えばAjaxでデータを送信してそのデータを使ってCSVを作成、ダウンロードしたいってなるとPHP側での実装が難しい場合もあるかもしれないので、そういう時はこのやり方が役に立つかもしれません。
 もしかしたら何か関連しているかも? 
 みんなからのコメント 
まだコメントはいただけてないみたい……