Fast CSV とは
Java用 の CSVライブラリは数多あります。例えば以下のようなものです。
しかしこれらは、積極的にメンテナンスされていなかったり、外部依存関係を含むため利用しにくかったりします。
Fast CSV は、外部依存無しでとても小さく使いやすいCSV操作ライブラリです。
初期コミットは 2015年4月 で、現在も積極的にメンテナンスが行われており、パフォーマンスについても、先に挙げたライブラリと比較してトップクラスです。
多くのCSVライブラリでは、Bean へのバインディングまでサポートしますが、Fast CSV ではこの部分は潔く割り切られており、シンプルなCSV操作に焦点が当てられています。
Fast CSV の使い方
依存定義は de.siegmar:fastcsv です。
dependencies {
implementation("de.siegmar:fastcsv:4.1.0")
}
CSV読み込み
CSV の読み込みには CsvReader を使います。
Path file = Paths.get("input.csv"); try (CsvReader<CsvRecord> csvReader = CsvReader.builder().ofCsvRecord(file)) { csv.skipLines(1); for (CsvRecord csvRecord : csvReader) { csvRecord.getFields().forEach(IO::println); } } catch (IOException e) { }
CsvReader は Iterable<T> を実装しているので、イテレートすれば CSV のレコードを逐次処理できます。
CsvRecord.stream() でStreamを得ることもできます。
public final class CsvReader<T> implements Iterable<T>, Closeable { // ... public Stream<T> stream() { // ... } }
CsvReader<T> の型引数 T が読み込んだCSVの1レコードを表現します。
これには、CsvRecord と NamedCsvRecord の2つがあり、ヘッダを保持するかどうかの違いがあります(NamedCsvRecord はヘッダ情報を保持)。
public class CsvRecord { final long startingLineNumber; final String[] fields; final boolean comment; } public final class NamedCsvRecord extends CsvRecord { private final String[] header; }
NamedCsvRecord からは、ヘッダ名を指定して値を取得することができます。
namedCsvRecord.getField("name of header");
CsvRecord と NamedCsvRecord のどちらで処理するかは、CsvReader のビルダーで指定します。
CsvReader<CsvRecord> csvReader = CsvReader.builder().ofCsvRecord(file); CsvReader<NamedCsvRecord> csv = CsvReader.builder().ofNamedCsvRecord(file);
CSVの最初の1行だけを取得する ofSingleCsvRecord() なんてのもあります。
CsvReader.builder().skipLines(int lineCount)
CSVの読み取り設定は CsvReader.builder() で指定します。
デフォルト設定は、RFC 4180 に準拠したものになっています。
CsvReader.builder()
.fieldSeparator(',') // フィールド区切り文字
.quoteCharacter('"') // 引用符
.commentStrategy(CommentStrategy.SKIP) // コメント処理方式(デフォルト:`CommentStrategy.NONE`)
.commentCharacter('#') // コメント文字(コメント処理方式が有効な場合)
.skipEmptyLines(true) // 空行をスキップするかどうか
.allowExtraFields(false) // 余分なフィールドを許可するかどうか
.allowMissingFields(false) // 欠落フィールドを許可するかどうか
.allowExtraCharsAfterClosingQuote(false) // 閉じ引用符後の余分な文字を許可するかどうか
.trimWhitespacesAroundQuotes(false) // 引用符周辺の空白をトリムするかどうか
.detectBomHeader(false) // BOMヘッダーを検出するかどうか
.maxBufferSize(16777216); // 最大バッファサイズ
CommentStrategy には以下を指定できます。
| 値 | 説明 |
|---|---|
| NONE | コメントを検出しない |
| READ | コメント行を検出し、コメント行を返す |
| SKIP | コメント行を検出し、コメント行は返さない |
CSVファイルをページ単位で読み取りたい場合は IndexedCsvReader があり、以下のように使うことができます(メモリ使用量を削減する目的ではなく、指定のページを読み取る目的)。
try (IndexedCsvReader<CsvRecord> csv = IndexedCsvReader.builder().pageSize(10).ofCsvRecord(file)) { CsvIndex index = csv.getIndex(); int lastPage = index.getPageCount() - 1; List<CsvRecord> csvRecords = csv.readPage(lastPage); }
Fast CSV では、Bean マッピングの特別な仕組みは用意されていないため、オブジェクトへの変換が必要な場合は以下のように行う。
record Person(long id, String firstName, String lastName) {} Person mapPerson(final NamedCsvRecord rec) { return new Person( Long.parseLong(rec.getField("ID")), rec.getField("firstName"), rec.getField("lastName") ); } try (var csv = CsvReader.builder().ofNamedCsvRecord(data)) { return csv.stream().map(this::mapPerson); }
CSV書き込み
CSV の書き込みには CsvWriter を使います。
Path file = Paths.get("output.csv"); try (CsvWriter csv = CsvWriter.builder().build(file)) { csv.writeRecord("header 1", "header 2") .writeRecord("value 1", "value 2") .writeComment("This is a comment") .writeRecord("value 3", "value 4"); }
CSVの書き込み設定は CsvWriter.builder() で指定します。
デフォルト設定は、RFC 4180 に準拠したものになっています。
CsvWriter.builder()
.fieldSeparator(',') // フィールド区切り文字
.quoteCharacter('"') // 引用符
.quoteStrategy(QuoteStrategies.REQUIRED) // 引用符戦略
.commentCharacter('#') // コメント文字
.lineDelimiter(LineDelimiter.LF) // 行区切り文字
.bufferSize(8_192) // バッファサイズ
.autoFlush(false) // 自動フラッシュ
;
QuoteStrategies には以下を指定できます。
| 値 | 説明 |
|---|---|
| ALWAYS | 内容に関わらず(空またはNULLのフィールドも含む)全てのフィールドを引用符で囲む |
| EMPTY | 空だがNULLではないフィールドを区別するため引用符で囲む |
| NON_EMPTY | 内容がある(空またはNULLでない)フィールドのみ引用符で囲む |
| REQUIRED | 引用符が必要とされる場合のみフィールドを囲む |
まとめ
現在最も人気のある Java 用の CSV 操作ライブラリである Fast CSV の利用方法について紹介しました。
多くのメジャーなプロジェクト(JUnit 6 など)での採用も増えており、今後の選択肢の一つになるでしょう。