業務でJavaのCSVライブラリを探して使用したので、備忘録としてまとめていきます。
#JavaのCSVライブラリの一覧(2018.01時点)
JavaでCSVライブラリは下記のものがあります。
- opencsv http://opencsv.sourceforge.net/
- OrangeSignalCSV http://orangesignal.github.io/orangesignal-csv/ ※2014年から更新が止まっています
- Super CSV http://super-csv.github.io/super-csv/index.html ※2015年から更新が止まっています
- Apache Commons CSV https://commons.apache.org/proper/commons-csv/
今回はopencsvを使用しました。
#環境
- Java 8
- Gradle 4.1
- opencsv 4.1
opencsv 4 はjava7以降が対応です。
また、opencsv3.xとopencsv4.xでは使い方が異なりますので注意してください。
#やりたいこと
こんな内容のCSVファイル(item.csv)の読取と出力を行います。
"商品ID","商品名","単価","数量"
001,"ポテトチップス",150,50
002,"チョコレート",100,30
003,"アイス",120,10
ポイントは3点です。
- ヘッダーつき(日本語)
- セパレータはカンマ
- 文字列はダブルクォート「"」でくくる
#準備
Gradleのdependencyにopencsvを追加します。
commons-loggingをexcludeしている場合はコメントアウトしてください。実行時に例外が発生します。
//下記の記載がある場合はコメントアウトが必要
configurations {
//all*.exclude module: 'commons-logging'
}
dependencies {
compileOnly group:'org.projectlombok', name:'lombok', version:'1.16.20'
//csv library
implementation group:'com.opencsv', name:'opencsv', version:'4.1'
implementation group:'commons-logging', name: 'commons-logging', version:'1.2'
}
また、CSVファイルの項目に合わせたEntityクラスを用意します。
Entityクラスのフィールドにアノテーションを付加することで、EntityクラスとCSVファイルのカラムを紐付けます。
アノテーションは取り込みパターンに応じて2種類から選択できます。
-
@CsvBindByName
ヘッダーカラムとフィールドを紐付け -
@CsvBindByPosition
入力順とフィールドを紐付け(ヘッダーつきのため今回は除外します)
@CsvBindByName
を使用する場合
package entity;
import com.opencsv.bean.CsvBindByName;
import lombok.Data;
/**
* 商品を管理するEntityクラス。
*/
@Data
public class ItemEntity {
@CsvBindByName(column = "商品ID", required = true)
private String id;
@CsvBindByName(column = "商品名", required = true)
private String name;
@CsvBindByName(column = "単価", required = true)
private int price;
@CsvBindByName(column = "数量", required = true)
private int amount;
}
@CsvBindByPosition
を使用する場合
package entity;
import com.opencsv.bean.CsvBindByPosition;
import lombok.Data;
/**
* 商品を管理するEntityクラス。
*/
@Data
public class ItemEntity2 {
@CsvBindByPosition(position = 0, required = true)
private String id;
@CsvBindByPosition(position = 1, required = true)
private String name;
@CsvBindByPosition(position = 2, required = true)
private int price;
@CsvBindByPosition(position = 3, required = true)
private int amount;
}
#CSV読み取り
CSV読み取りのコードサンプルです。
CsvToBean<ItemEntity> csvToBean = new CsvToBeanBuilder<ItemEntity>(reader).withType(ItemEntity.class).build();
List<ItemEntity> items = csvToBean.parse();
CsvToBeanBuilder クラスを使用します。
CsvToBeanBuilder#withType
メソッドまたは CsvToBeanBuilder#withMappingStrategy
メソッドを必ず呼び出してください。
セパレータを指定する場合は CsvToBeanBuilder#withSeparator
メソッドを使用してください。
コードサンプルでは @CsvBindByName
を使用したEntityクラスを使用しています。
@CsvBindByPosition
を使用したEntityクラスの場合、ヘッダーがなければ同様の結果が得られます。
#CSV出力
CSV出力のコードサンプルです。
StatefulBeanToCsv<ItemEntity> beanToCsv = new StatefulBeanToCsvBuilder<ItemEntity>(writer).build();
beanToCsv.write(beans);
StatefulBeanToCsvBuilder クラスを使用します。
セパレータを指定する場合は CsvToBeanBuilder#withSeparator
メソッドを使用してください。
@CsvBindByName
を使用したEntityクラスを使用した場合、ヘッダーは自動で付加されますが、カラムの順番はヘッダーの文字コード順(?)となるようです。
@CsvBindByPosition
を使用したEntityクラスを使用した場合、ヘッダーは自動で付加されません。
#サンプルコードと実行結果
import java.io.Reader;
import java.io.Writer;
import java.util.List;
import com.opencsv.bean.CsvToBean;
import com.opencsv.bean.CsvToBeanBuilder;
import com.opencsv.bean.StatefulBeanToCsv;
import com.opencsv.bean.StatefulBeanToCsvBuilder;
import com.opencsv.exceptions.CsvException;
/**
* CSVファイルとのやり取りを行うDaoクラス。
*/
public class ItemFileDao {
public void write(Writer writer, List<ItemEntity> beans) throws CsvException {
StatefulBeanToCsv<ItemEntity> beanToCsv = new StatefulBeanToCsvBuilder<ItemEntity>(writer).build();
beanToCsv.write(beans);
}
public List<ItemEntity> read(Reader reader) throws CsvException {
CsvToBean<ItemEntity> csvToBean = new CsvToBeanBuilder<ItemEntity>(reader).withType(ItemEntity.class).build();
return csvToBean.parse();
}
}
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import com.opencsv.exceptions.CsvException;
public class Main {
public static void main(String[] args) {
ItemFileDao csvFileDao = new ItemFileDao();
try (Reader reader = Files.newBufferedReader(Paths.get("item.csv"));
Writer writer = Files.newBufferedWriter(Paths.get("item2.csv"));) {
List<ItemEntity> items = csvFileDao.read(reader);
items.forEach(System.out::println);
csvFileDao.write(writer, items);
} catch (IOException e) {
e.printStackTrace();
} catch (CsvException e) {
e.printStackTrace();
}
}
}
ItemEntity(id=001, name=ポテトチップス, price=150, amount=50)
ItemEntity(id=002, name=チョコレート, price=100, amount=30)
ItemEntity(id=003, name=アイス, price=120, amount=10)
"単価","商品ID","商品名","数量"
"150","001","ポテトチップス","50"
"100","002","チョコレート","30"
"120","003","アイス","10"