Java Tips | I/O・ネットワーク:CSV読み込み

Java Java
スポンサーリンク

業務で「CSV読み込みユーティリティ」が必要になる理由

CSVは、業務システムで一番よく使われるデータ形式のひとつです。 顧客一覧、売上データ、設定情報、外部ベンダーとの連携ファイル——どれもだいたい CSV です。

そのたびに「行を split して…」「ダブルクォートをどう扱うか…」を毎回手書きしていると、 微妙なバグや仕様差が積み重なって、あとから地獄になります。

だからこそ、「CSV読み込み」をユーティリティとしてきちんと切り出しておくことが、 業務・実務ではかなり重要になります。

ここでは、初心者向けに噛み砕きつつ、実務で通用するレベルまで丁寧に説明します。

CSVの基本ルールをざっくり押さえる

行と列のイメージ

CSVは「行の集まり」であり、 各行は「カンマ区切りの値の集まり」です。

ざっくり言うと、

  • 1 行 = 1 レコード
  • 1 列 = 1 項目

というイメージです。

しかし、ここで初心者がよくハマるポイントがあります。

カンマそのものを値に含めたい場合

例えば、次のような値を CSV に入れたいとします。

東京都,江戸川区

このまま書くと、カンマで列が分割されてしまいます。 そこで、CSVでは「ダブルクォートで囲む」というルールがあります。

"東京都,江戸川区"

こうすると、カンマは「値の一部」として扱われます。

改行を値に含めたい場合

同じように、改行を含む値もダブルクォートで囲みます。

"1行目
2行目"

このようなルールがあるので、 単純に line.split(",") では正しく CSV を読めません。

シンプルなCSV読み込みユーティリティ(「割り切り版」)

「カンマを含まない」「ダブルクォートを使わない」前提ならこれで十分

まずは、ルールがシンプルな CSV(カンマもダブルクォートも含まない)を読むユーティリティから見てみましょう。

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;

public class SimpleCsvReader {

    public static List<String[]> read(Path path, Charset charset) throws IOException {
        List<String[]> rows = new ArrayList<>();
        for (String line : Files.readAllLines(path, charset)) {
            if (line.isEmpty()) {
                continue; // 空行はスキップ
            }
            String[] columns = line.split(",", -1); // -1 で末尾の空列も保持
            rows.add(columns);
        }
        return rows;
    }

    public static List<String[]> readUtf8(Path path) throws IOException {
        return read(path, StandardCharsets.UTF_8);
    }
}
Java

使い方の例です。

Path path = Path.of("data/simple.csv");
List<String[]> rows = SimpleCsvReader.readUtf8(path);

for (String[] cols : rows) {
    System.out.println("1列目: " + cols[0] + ", 2列目: " + cols[1]);
}
Java

ここで重要なのは、「split-1 を渡している」ことです。 split(",", -1) とすると、末尾の空列もきちんと保持されます。

例えば、

コード

a,b,

という行は、["a", "b", ""] になります。

これをしないと、 「最後の列が空のときに列数が減る」という地味なバグが起きます。

ちゃんとしたCSVを読むときの考え方

ダブルクォートとカンマを正しく扱う必要がある

実務では、「カンマを含む値」「改行を含む値」「ダブルクォートを含む値」が普通に出てきます。

CSVの正式ルールでは、次のような扱いになります。

  • 値にカンマや改行を含めたいときは、値全体を "..." で囲む
  • 値の中に " を含めたいときは、"" と二重に書く

例:

"東京都,江戸川区","彼は""天才""と呼ばれた"

これを自前でパースするのは、初心者にはかなりしんどいです。 実務でも、ここを手書きするのはおすすめしません。

ライブラリを使う、という選択肢

業務では、CSVパーサのライブラリ(OpenCSV など)を使うのが一般的です。 ここではコード詳細には踏み込みませんが、考え方だけ押さえておきます。

  • ライブラリは「CSVの正式ルール」をきちんと実装してくれている
  • 自前実装よりも、仕様差やバグのリスクが圧倒的に少ない
  • ユーティリティは「ライブラリの使い方を隠すラッパー」として設計する

こうしておくと、業務コード側は「CSV読み込みユーティリティを呼ぶだけ」で済み、 CSVの細かい仕様を意識せずに済みます。

CSV読み込みユーティリティで絶対に意識してほしいポイント

文字コードを必ず指定する

CSV読み込みで一番やってはいけないのが、 文字コードを指定せずに new String(bytes)Files.readAllLines(path) を使うことです。

文字コードを指定しないと、OSやJVMのデフォルトに依存し、 開発環境と本番環境で挙動が変わる原因になります。

業務では、ほぼ常に UTF-8 に統一するのがおすすめです。

Files.readAllLines(path, StandardCharsets.UTF_8);
Java

このように、必ず Charset を明示してください。

BOM(UTF-8 BOM)の存在を意識する

CSVの先頭に BOM が付いていると、 1 列目の先頭に「見えないゴミ」が混ざり、ヘッダ名が一致しなくなります。

例: 本来 → id,name,email BOM付き → id,name,email

これを防ぐために、 「CSV読み込みユーティリティの中で BOM を除去する」という設計も有効です。

すでに話した BOM除去ユーティリティと組み合わせると、 CSV読み込みがかなり安定します。

行数・列数のチェックを入れる

CSVは「静かに壊れる」ことが多い形式です。 例えば、途中の行だけ列数が違う、 空行が紛れ込んでいる、 ヘッダ行とデータ行の列数が合わない——などです。

ユーティリティ側で次のようなチェックを入れておくと、 業務コードが安心して使えるようになります。

  • 1 行目をヘッダとして扱い、列数を基準にする
  • 2 行目以降で列数が違う行があればログや例外にする
  • 空行はスキップするかどうかを方針として決める

CSV読み込みユーティリティの実務的な使いどころ

外部ベンダーからのデータインポート

外部ベンダーから送られてくる顧客データや売上データは、 ほぼ確実に CSV です。

ここで「毎回その場で CSV を手書きパースする」のではなく、 共通の CSV読み込みユーティリティを通すことで、 文字コード・BOM・列数チェックなどを一括で管理できます。

管理画面からの CSVアップロード

管理画面で「CSVをアップロードして一括登録」という機能を作るとき、 CSV読み込みユーティリティがあるかどうかで、 バグの量と保守性が大きく変わります。

アップロード処理は「ファイルを受け取る」ことに集中し、 中身の解釈はユーティリティに任せる—— この分離ができていると、テストもしやすくなります。

まとめ:CSV読み込みユーティリティで身につけてほしい感覚

CSV読み込みユーティリティは、「業務データを安全に、安定して取り込む」ための入り口です。 そこには、次のような大事な感覚が詰まっています。

文字コード(UTF-8など)を必ず明示して読むこと。 単純な split(",") ではなく、列数や空列の扱いを意識すること。 BOM の存在を前提にし、必要なら除去してから読むこと。 CSVの正式ルール(ダブルクォート・カンマ・改行)を自前で頑張りすぎず、ライブラリ+ユーティリティで包むこと。

もし今、あなたのプロジェクトに「画面ごと・機能ごとにバラバラな CSV読み込みコード」が散らばっているなら、 それを一度「業務用 CSV読み込みユーティリティ」に集約できないか眺めてみてほしいです。

そこから先は、外部データの取り込みが、ぐっと安定していきます。

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