概要
CSV はシステム間のデータ連携、マスタ一括登録、帳票用データの受け渡しなど、業務システムでもっとも頻繁に扱うファイル形式のひとつです。一見単純なフォーマットですが、フィールド内にカンマを含む場合のダブルクォート対応、ヘッダー行の扱い、大容量ファイルのメモリ効率など、実務では意外と考慮点が多くなります。この記事では、標準 API だけで CSV の読み書きと簡易パースを実装し、大容量ファイルにも対応できる Files.lines() によるストリーム処理まで整理します。
使いどころ
経理部門が用意した商品マスタの CSV を読み込み、DB に一括登録する
月次売上データを CSV で出力し、他システムや Excel との連携に使う
取込処理でカンマ入りの住所フィールドを含む CSV を安全にパースする
コード例
import java.io.IOException;
public class CsvReadWrite {
/** CSV ファイルを読み込む */
public static List<String> readCsv(Path path) throws IOException {
return Files.readAllLines(path, StandardCharsets.UTF_8);
}
/** CSV ファイルに書き込む */
public static void writeCsv(Path path, List<String> lines) throws IOException {
Files.write(path, lines, StandardCharsets.UTF_8);
}
/** ダブルクォート対応の簡易 CSV パーサー */
public static String[] parseCsvLine(String line) {
List<String> fields = new ArrayList<>();
StringBuilder sb = new StringBuilder();
boolean inQuotes = false;
for (int i = 0; i < line.length(); i++) {
char c = line.charAt(i);
if (c == '"') {
inQuotes = !inQuotes;
} else if (c == ',' && !inQuotes) {
fields.add(sb.toString());
sb = new StringBuilder();
} else {
sb.append(c);
}
}
fields.add(sb.toString());
return fields.toArray(String[]::new);
}
public static void main(String[] args) throws IOException {
var csvLines = List.of(
"name,price,category",
"apple,100,fruit",
"\"milk, low-fat\",200,dairy"
);
var tempFile = Files.createTempFile("sample", ".csv");
tempFile.toFile().deleteOnExit();
writeCsv(tempFile, new ArrayList<>(csvLines));
readCsv(tempFile).stream().skip(1).forEach(line -> {
var fields = parseCsvLine(line);
System.out.println("name=" + fields[0] + ", price=" + fields[1]);
});
try (var stream = Files.lines(tempFile, StandardCharsets.UTF_8)) {
stream.skip(1)
.map(line -> parseCsvLine(line)[0])
.forEach(System.out::println);
}
}
}Version Coverage
Files.readAllLines() / Files.write() で簡潔に読み書き可能。var とメソッド参照の組み合わせで記述量が減る。
// Java 17: Files.readAllLines() で一括読み込み
var lines = Files.readAllLines(path, StandardCharsets.UTF_8);
lines.stream().skip(1).forEach(line -> {
var fields = parseCsvLine(line);
});Library Comparison
注意点
標準 API には CSV パーサーが含まれないため、ダブルクォート内の改行を扱うには自前の実装が必要。複雑な CSV は Apache Commons CSV の利用を検討すること。
Files.readAllLines() は全行をメモリに読み込むため、数十万行を超える CSV には Files.lines() のストリーム処理を使うこと。
CSV の改行コードが CRLF・LF 混在していると、行末に \r が残ってパースが壊れることがある。trim() での除去を忘れないこと。
BOM(Byte Order Mark)付き UTF-8 ファイルを読むと先頭フィールドに \uFEFF が混入する。必要に応じて除去処理を入れること。
FAQ
1行目を parseCsvLine() で分割してフィールド名配列を作り、2行目以降のインデックスと対応づけるのが標準的な方法です。
Files.lines() で Stream を取得し、1行ずつ処理すればメモリ消費を抑えられます。try-with-resources で確実に close してください。
区切り文字をカンマからタブに変えるだけで対応できます。ダブルクォートのルールも CSV と同様に適用されます。