====== Introduction ====== **CSV**(comma-separated variables)はデータをカンマ(",")で区切って並べたファイル形式で 主に表計算ソフトやデータベースソフトがデータを保存する時に使うformatである。\\ 汎用性が高く、多くの電子手帳やワープロソフトなどでも利用できるため、異なる種類のアプリケーションソフト間の データ交換に使われることも多い。\\ 実体はテキストファイルであるため、テキストエディタやワープロなどで開いて直接編集することも可能。\\ 今回はJavaでCSV形式を書き出す方法を紹介する。 {{keywords> comma CR-LF 改行文字 カンマ}} ~~META: title=How to write out a list of data with a CSV format ~~ ===== CSV Specification ===== you can review an original version from [[study:java:csv#Reference|specification]] CSV file -------- file ::= [ header ] { line } header ::= [ { entry separator } entry ] newline line ::= [ { entry separator } entry ] newline entry ::= character+ | { character* " separator " } | " entry newline entry " newline ::= \n separator ::= , character ::= a|b|..|A|B|..|0|1|.. escapedQuote ::= "" ===== Text Output ===== まずはCSV形式に合う文字列を書き出すことだが、便宜のため書き出す項目がリストに入っていると想定しよう。\\ 項目が入るリストと列を格納するリストが必要となる。リストの中にリストが入っているイメージだ。\\ ロジックは意外と簡単だ。リストの項目を順番ごとに**%%StringBuffer%%**に追加していきながらカンマをくっつけることだ。\\ リストの最後の項目にあったらカンマを削って改行文字を入れ替えれば良い。\\ ソースコードを次に示す。 public static String getCsvOutputString(ArrayList list) throws UnsupportedEncodingException { StringBuffer _sb = new StringBuffer(); Iterator _iterator = list.iterator(); if(list == null) return ""; if(list.size() == 0) return ""; while(_iterator.hasNext()) { ArrayList _line = (ArrayList) _iterator.next(); Iterator _lineIterator = _line.iterator(); while(_lineIterator.hasNext()) { String _data = (String) _lineIterator.next(); if(_data != null) { _sb.append(_data); } _sb.append(","); } //,を削って改行文字を挿入する if(_sb.length() > 0) { if(_sb.substring(_sb.length() - 1).equals(",")) { _sb.deleteCharAt(_sb.length() - 1); _sb.append(System.getProperty("line.separator")); } } } System.out.println(_sb.toString()); return _sb.toString(); } ===== Set Response Header ===== CSVファイルで出力するにはブラウザにファイルの形式や名前、容量などを知らせる必要がある。\\ response.setContentType("application/octet-stream;charset=UTF8"); response.setHeader("Content-Disposition", "attachment; filename="+ filename)); filenameはダウンロードする際のファイル名を指定する。 ===== Write out to output stream ===== %%HttpServletResponse%%から**%%OutputStream%%**を取得してこのストリムに 前のリストから作り出した文字列を出力する。\\ この際、文字のEncodingを指定できる。仮に日本語を出力する場合、**UTF-8**では文字化けてしまうので **”SJIS”**で指定しなければならない。\\ 複数の言語が混じっていたら最善の選択はUTF-8だと思うがテストして見なかったのでここはパス try{ OutputStream _os = response.getOutputStream(); OutputStreamWriter _out = new OutputStreamWriter(_os, "UTF-8"); BufferedWriter _bout = new BufferedWriter(_out); _bout.write(outputText); _bout.close(); _out.close(); }catch(IOException ioe){ //handle exception } ===== Issue ===== **CSV**の仕様上、カンマで区切る為、もしカンマが含まれていたら不具合が発生する。\\ また、列の最後に改行文字(CR-LF)((CR:Carriage Return, LF:Line Feed))が入れる為、もし改行文字が入っている場合、問題になることがある。((excelデータインポート機能を利用してcsvを開く場合、改行文字で改行されてしまうので、問題になる。 しかし、ダブルクリックで開く場合は、改行文字をダブルクオーテーション(")で囲むと問題ない。))\\ そこで、この問題に対応する**Tip**を紹介したいと思う。\\ カンマ(,)に関してはCSVの仕様(specification)でダブルクオーテーション(")で囲むようになっている。\\ 改行文字の場合は**CR-LF(\r\n)**ではなく**LF(\n)**を使うことを奨励している。 ([[wp>Comma-separated_values]]に行けばこれらの例が載っている。)\\ 以上二つの点を踏まえて対応する方法がこれだ。 public static String escapeCsvStr(String string) { String _ret = getNullString(string); StringBuffer temp = new StringBuffer(); if(!"".equals(_ret)) { temp.append("\"").append(_ret).append("\""); //改行文字の削除 _ret = temp.toString(); _ret = _ret.replaceAll("\r\n", "\n"); } return _ret; } リストの項目全体をダブルクォーテーション(")囲んで中のCR-LFをLFで入れ替える処理だ。 ===== Reference ===== -[[http://en.wikipedia.org/wiki/Comma-separated_values]] : Wikipedia -[[http://supercsv.sourceforge.net/csvSpecification.html]] : CSV Specification ~~DISCUSSION~~