OpenCSV


OpenCSVは、JavaでCSVファイルを読み書きできるApache2.0 ライセンスのオープンソースのCSVParserライブラリーです。

ファイルサイズは11kbとコンパクトですので、GAEからAndroidさらにはGWTなどで活用できると思います。
速度も問題ないと思います。
(GWTで使うにはReader・Writerクラスを改造する必要があります。修正したもの)

CSVの形式

デフォルトの形式

カンマ区切りで、データーをダブルクォート(")で囲み、データー中のダブルクォートはダブルクォートでエスケープされます。

"1","名前","7","テキスト","22"

 入力(スペース区切り) 出力
 データー1 データー2 "データー1","データー2"
 he say "hello". "he say ""hello""."
 " , """",","


指定出来るCSV形式

区切りキャラクター及び、行文字を指定できます。
囲むキャラクターを指定したり、囲まないよう指定もできます。ただし、指定なしだと区切り文字はエスケープされないので問題が起こりやすいです。
囲むキャラクターのエスケープキャラクターも指定できます。エスケープ無しも可能(デフォルトでは"は""となるが、これを\"という風に指定可能)

  コンストラクタ csv形式
 囲み有りカンマ区切り new CSVWriter(writer) "データー","1"
 囲みなしタブ区切り new CSVWriter(writer,'\t',CSVWriter.NO_QUOTE_CHARACTER) データー 1

できない/やり方がわからないこと

囲むキャラクターは無しだけど、データー中の区切り文字はエスケープするやり方がわからない。

出力時、囲む必要がないデーターは囲まない方法がわからない。

無駄に囲むと逆に読みにくかったりもしますし、データーも大きくなります。
ただし読み込み時はGoogle スプレットシートのCSV出力でもある、この形式は読み込める。
例 (1,"名前=""X""",7,テキスト,22)

改行を含む文字の改行コード指定

デフォルトの改行は\nです。改行を指定できないと、Windowsでそのまま出力するときに不便になります。
ソースコードを見てみますと、Parser中に"\n"を追加していました。
ですので、Loop内で、csv[i]=csv[i].replace("\n", "\r\n"); という風に自分で\nを置き換えるといいですね。

使い方

読み込み

CSVReaderを使ってReaderクラスから読み込みます。区切り文字や囲み文字などは、コンストラクタで指定します。先頭の行をスキップも指定できます。詳しくはCSVReader API参照
public CSVReader(java.io.Reader reader) public CSVReader(java.io.Reader reader,char separator) public CSVReader(java.io.Reader reader,char separator,char quotechar) public CSVReader(java.io.Reader reader,char separator,char quotechar,int line)
あとは、まとめて読み込む場合は、
(Bufferの関係かどうかわかりませんが、時々、変なことがありますので、私はreadNextしか使っていません)
CSVReader reader=new CSVReader(new FileReader(new File("null.csv")));
List<String[]> datas=reader.readAll();

行ごとに読み込む場合は、以下のようになります。
CSVReader reader=new CSVReader(new FileReader(new File("null.csv")));
String[] csv=reader.readNext();


また、Google ドキュメントのSpreadsheetsのCSV保存時とかで使われる、必要がない項目はダブクォートでくくられない形式も読み込めます。

 1,データー,"book,map and cat"

CSVReaderの文字コード

CSVReaderではReaderを引数にしますので、Readerが文字コードをどうにかします。
文字コードを明確に指定する場合は、InputStreamReaderを作成して文字コードを指定します。

以下はエンコードがUTF-8で1行目がヘッダーなのでスキップしてCSVファイルを読み込むCSVReaderの作り方です。
InputStream input=new FileInputStream(path);
InputStreamReader ireader=new InputStreamReader(input, "UTF-8");
CSVReader reader = new CSVReader(ireader,',','"',1);

書き込み

CSVWriterクラスを使います。区切り文字や囲み文字などはコンストラクタで指定します。
最後にflush()かclose()しないと出力されないことがあります。詳しくは、CSVWriter APIを参照

行単位で書き出す場合、writeNextを使います。
String test[]={"\"",","};
CSVWriter writer = new CSVWriter(new PrintWriter(System.out));
writer.writeNext(test);
writer.flush()

まとめて書き出す、writeAll(List<String[]>)というメソッドもあります。

単純に分割

複数の行でなく、単純な行の場合は、CSVParserを使うのが楽です。
CSVReaderでも内部ではCSVParserを呼び出しています。

使い方は単純でインスタンスを作成してタブやカンマなどの分割キャラクターや、シングルやダブルのクォートキャラクターを指定するだけです。
何も指定しないと、デフォルトのカンマとダブルクォートのパーサーが出きます。

CSVParser()
CSVParser(char
separator
分割キャラクター)
CSVParser(char 分割キャラクター,char クォートキャラクター)


あとは、parseLineを呼び出します。よく使う場合は、s
String[] vs = csvparser.parseLine(line);


 ちなみに、Stringのsplitで分割は手軽だけど、注意が必要です。
"a,,," とかの文字列ですと、配列の長さ1つになります。
普通の時は問題起きにくいですが、HTMLのTableのTDとか作っていると問題になります。



CSVからJavaBeanを生成する

非常に簡単にCSVからJavaBeanを作成出きます。

例えばは、以下のようなクラスに変換したい場合
public class City {
private String name="";
private int score;
private String comment="";

//getter setterは略
}

クラスとコラム名を指定すれば、以下のようにすれば、簡単にJavaBeanになります。
コラム名に無効な値を入れておくことで、項目をスキップすることもできます。
ColumnPositionMappingStrategy strat = new ColumnPositionMappingStrategy();
strat.setType(City.class);
String[] columns = new String[] {"name", "score", "comment"};
strat.setColumnMapping(columns);

CsvToBean csv = new CsvToBean();
List<City> list = csv.parse(strat, reader);

 入力CSV Tokyo,100,great
 生成されたBeanString name は "Tokyo"
int scoreは100
String commentは"great"


ただし、途中の数字の項目が空の時は、NumberFormatExceptionが発生してしまうので気をつけてください。
例えば2つ目の項目が数字だとすると以下のようになります。
NewYork,10,good    //ok
Tokyo,    //エラー "" を数字に変換しようとする
Pari,,good    //エラー ""を数字に変換しようとする
London    //ok 数字を読み取らない

ただこの場合、文字列を受け取る専用のメソッドを用意して、そのsetter名に合わせて
コラムンの名前を{"name", "scoreString", "comment"}を変更することで回避できます。
もちろんOpenCSVはオープンソースのCSVParserですので、APIのソースコードをエラーでないように変更してもいいのですが

public class City {
private int score;

public void setScoreString(String scoreString) {
try{
score=Integer.parseInt(scoreString);
}catch(Exception e){
//e.printStackTrace();
}
}

}

その他

ResultSetをCSVとして出力したりもできます。

GWTでOpenCSVを使う

そのまま使う

GWT-CSVParser  - GWTでも動くようにしてみました。

改造して使う

GWTではCSVParserのOpenCSVはそのまま使えません。
GWTではReader・Writerクラスが使えないので、こんな感じに改造する必要があります。

    private int lineIndex;
    private String[] lines;

    
    public CSVReader(String text, char separator, char quotechar, char escape, int line, boolean strictQuotes) {
    	text=text.replace("\r\n", "\n");
        text=text.replace("\r", "\n");
        lines=text.split("\n");
       
        this.parser = new CSVParser(separator, quotechar, escape, strictQuotes);
        this.skipLines = line;
    }

    private String getNextLine() throws IOException {
    	if (!this.linesSkiped) {
            for (int i = 0; i < skipLines; i++) {
                lineIndex++;
            }
            this.linesSkiped = true;
        }
    	
    	
        String nextLine = null;
        if(lines.length>lineIndex){
        	nextLine=lines[lineIndex];
        	
        	lineIndex++;
        }
        if (nextLine == null) {
            hasNext = false;
        }
        return hasNext ? nextLine : null;
    }

AndroidでOpenCSVを使う

普通に使えます。コーテションで囲まれている文字分割や複数行のCSV解析には便利です。
単純なカンマ区切りのぶんかつの場合、CSVParserのparseは、文字列のsplit()に比べて少し遅いです。

 ただ、複数行からなるデーターの解析では、普通の方法ではreadLine()するにに、BufferedReaderまで作る必要があるためか
CSVReaderの方が速いケースもあります。
AndroidのIO関連は遅いのでなるべく避けたい所です。

OpenCSVと文字化け

基本的には、Readerから読み込み、Writerに書きだすので、OpenCSVが原因で文字化けは起こりえません。
ただし、FileReaderとかでいきなり読み込むと文字コードの自動判定失敗することがあるので
InputStreamReaderで、文字コードを指定して読み込みましょう。文字コードの判別が怪しい時は、juniversalchardet (UTF-8は先頭のゴミに注意)とか使いましょう。
あと、SJISだと、Windows-31J/MS932とは、違いがあるので気を付けましょう。SJISで読み込んだ場合、とか化けるのは、これがWindows-31J 機種依存コードだからです。




Comments