CSVファイルを手軽に読み込む ~Apache Commons CSV~

Java
この記事は約8分で読めます。
広告

この記事の目標

JavaでCSVファイルを読み書きするライブラリに「Apache Commons CSV」がある。

また、CSVファイルでよく知られた共通フォーマットとして「RFC 4180」がある。しかしこの共通フォーマットが定義される前から広く使われていたためか、CSVファイルには多くの「方言」があるらしい。

この記事ではApache Commons CSVがRFC 4180の仕様をどの程度カバーしているかを調査

comma-separated values(略称:CSV)は、テキストデータをいくつかのフィールド(項目)に分け、区切り文字であるカンマ「,」で区切ったデータ形式。拡張子は .csv、MIMEタイプは text/csv。

出典: ja.wikipedia.org

サンプルCSVとサンプルプログラム

下記のCSVとプログラムを用いる。また、Apache Commons CSVのバージョンは1.8(2020年2月6日リリース)を使用する。

サンプルCSVの#で始まる行はコメント行である。これはCSVFormat#withCommentMarkerで#をコメント行を表す文字に指定しているためである。
# ヘッダ行
フィールド1,フィールド2,フィールド3,フィールド4
# 各レコードは改行コードで区切られる
改行,コード,で,区切られる
# スペースはそのまま読み込む
スペースなし, 前にスペース,後ろにスペース , 前後にスペース 
# 二重引用符はあってもなくても良い
"二重引用符は",あっても,"なくても",良い
# 改行コード、コンマを含むフィールドは二重引用符で囲む
"改行
改行","コンマ,コンマ",a,b
# 二重引用符内の二重引用符は二重引用符を重ねてエスケープ
"二重引用符""囲まれた""二重引用符",a,b,c
# 最終行は改行コードなしでも良い
最終行は,改行コード,なしでも,良い
try (CSVParser parser = CSVFormat.DEFAULT
		.withCommentMarker('#')
		.withFirstRecordAsHeader()
		.parse(Files.newBufferedReader(csvFilePath))) {
	for (CSVRecord record : parser) {
		System.out.println(record);
	}
}

実行結果の確認

実行結果の全体は下記のとおり。以下で1つ1つの結果について確認していく。

以降CSVファイルの1行1行を「レコード」、コンマで区切られたデータを「フィールド」と呼ぶ。
CSVRecord [comment='各レコードは改行コードで区切られる', recordNumber=1, values=[改行, コード, で, 区切られる]]
CSVRecord [comment='スペースはそのまま読み込む', recordNumber=2, values=[スペースなし,  前にスペース, 後ろにスペース ,  前後にスペース ]]
CSVRecord [comment='二重引用符はあってもなくても良い', recordNumber=3, values=[二重引用符は, あっても, なくても, 良い]]
CSVRecord [comment='改行コード、コンマを含むフィールドは二重引用符で囲む', recordNumber=4, values=[改行
改行, コンマ,コンマ, a, b]]
CSVRecord [comment='二重引用符内の二重引用符は二重引用符を重ねてエスケープ', recordNumber=5, values=[二重引用符"囲まれた"二重引用符, a, b, c]]
CSVRecord [comment='最終行は改行コードなしでも良い', recordNumber=6, values=[最終行は, 改行コード, なしでも, 良い]]

最初にヘッダ行があっても良い

CSVファイルの最初の行に各フィールドの名前を示すヘッダ行があっても良い。

Apache Commons CSVでは、CSVFormat#withFirstRecordAsHeaderを指定することでヘッダ行を読み飛ばすことができる。

# ヘッダ行
フィールド1,フィールド2,フィールド3,フィールド4

ヘッダ行を読み込んでおくと、レコードから必要なフィールドをフィールド名指定で取得できる。以下はサンプルCSVの4行目のレコードから取得した例。

record.get("フィールド1")
	==> 改行
record.get("フィールド2")
	==> コード
record.get("フィールド3")
	==> で
record.get("フィールド4")
	==> 区切られる

各レコードは改行コードで区切られる

CSVの各レコードは改行コードで区切られる。

# 各レコードは改行コードで区切られる
改行,コード,で,区切られる
CSVRecord [comment='各レコードは改行コードで区切られる', recordNumber=1, values=[改行, コード, で, 区切られる]]

スペースはそのまま読み込む

スペースを含むフィールドはそのまま読み込みトリムはしない。

実行結果ではデータ整形のためのスペースが別途入るため少々分かりにくいが、前後のスペースがそのまま読み込まれている。
# スペースはそのまま読み込む
スペースなし, 前にスペース,後ろにスペース , 前後にスペース 
CSVRecord [comment='スペースはそのまま読み込む', recordNumber=2, values=[スペースなし,  前にスペース, 後ろにスペース ,  前後にスペース ]]

二重引用符はあってもなくても良い

各フィールドを二重引用符(ダブルコーテーション)で囲っても良いし囲わなくても良い。

二重引用符が必須なパターンもある。詳しくは後述。
# 二重引用符はあってもなくても良い
"二重引用符は",あっても,"なくても",良い 
CSVRecord [comment='二重引用符はあってもなくても良い', recordNumber=3, values=[二重引用符は, あっても, なくても, 良い]]

改行コード、コンマを含むフィールドは二重引用符で囲む

改行コードやコンマといった、レコード区切りやフィールド区切りに使われる文字をフィールド内に含めるときは、必ず二重引用符で囲わなければならない。そうしないと意図していないところでレコード区切りやフィールド区切りとみなされてしまうためである。

実行結果では少し分かりにくいが、「コンマ,コンマ」で1つのフィールドである。
# 改行コード、コンマを含むフィールドは二重引用符で囲む
"改行
改行","コンマ,コンマ",a,b
CSVRecord [comment='改行コード、コンマを含むフィールドは二重引用符で囲む', recordNumber=4, values=[改行
改行, コンマ,コンマ, a, b]]

二重引用符内の二重引用符は二重引用符を重ねてエスケープ

二重引用符の中に二重引用符を入れる場合は、二重引用符を2回続けてエスケープしなければならない。

二重引用符を2回続けても、表示される二重引用符は1つだけである。
# 二重引用符内の二重引用符は二重引用符を重ねてエスケープ
"二重引用符""囲まれた""二重引用符",a,b,c
CSVRecord [comment='二重引用符内の二重引用符は二重引用符を重ねてエスケープ', recordNumber=5, values=[二重引用符"囲まれた"二重引用符, a, b, c]]

最終行は改行コードなしでも良い

各レコードは改行コードで区切るが、最終行は改行コードがなくても良い。

# 最終行は改行コードなしでも良い
最終行は,改行コード,なしでも,良い
CSVRecord [comment='最終行は改行コードなしでも良い', recordNumber=6, values=[最終行は, 改行コード, なしでも, 良い]]

まとめ

Apache Commons CSVがRFC 4180の主要な仕様をすべて満たすことが確認できた。これを使えば主要なCSVファイルは読み込むことができそうだ。

コメント

タイトルとURLをコピーしました