一番人気の Java CSVライブラリ Fast CSV


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) { }

CsvReaderIterable<T> を実装しているので、イテレートすれば CSV のレコードを逐次処理できます。 CsvRecord.stream() でStreamを得ることもできます。

public final class CsvReader<T> implements Iterable<T>, Closeable {
    // ...
    public Stream<T> stream() {
        // ...
    }
}


CsvReader<T> の型引数 T が読み込んだCSVの1レコードを表現します。 これには、CsvRecordNamedCsvRecord の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");


CsvRecordNamedCsvRecord のどちらで処理するかは、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 など)での採用も増えており、今後の選択肢の一つになるでしょう。