首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 程式設計入門

Java CSV 解析實戰:從 BufferedReader 手工分割到生產級庫選型指南

     編輯:Java 程式設計入門

前言:看似簡單實則暗坑重重的 CSV

CSV (Comma-Separated Values) 是最普遍的表格資料交換格式,**但 RFC 4180 規範包含引號包裹、逗號轉義、換行符嵌入、雙引號跳脫**等細節。手工 split(",") 僅能處理最簡單情況。本文從基礎手工解析出發,逐步引導至生產級庫選型。

基礎手工解析(僅適用簡單 CSV)

import java.io.*;
import java.util.*;

public class HandleCsv {
    public static void main(String[] args) throws IOException {
        try (BufferedReader br = new BufferedReader(
                new InputStreamReader(new FileInputStream("test.csv"), "UTF-8"))) {

            String line;
            while ((line = br.readLine()) != null) {
                // 簡單分割:不處理引號內逗號、換行等
                String[] fields = line.split(",");
                String name = fields[0].trim();
                String age = fields[1].trim();
                String birthday = fields[2].trim();
                System.out.printf("%s\t%s\t%s%n", name, age, birthday);
            }
        }
    }
}

測試資料

alice, 22, 1992/03/05
bob, 33, 1981/11/21
cart, 40, 1974/07/13

手工解析的致命缺陷

CSV 特性範例split(",") 結果正確結果
欄位含逗號"台北, 信義區", 253 欄位2 欄位
欄位含換行"第一行\n第二行", 30讀成兩行1 列 2 欄
欄位含雙引號"He said \"Hi\"", 20亂碼1 欄位
前後空白 alice , 22含空白需 trim

生產級 CSV 庫推薦與代碼範例

1. Apache Commons CSV —— 最輕量、標準庫風格

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-csv</artifactId>
    <version>1.10.0</version>
</dependency>
import org.apache.commons.csv.*;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;

public class CommonsCsvDemo {
    public static void main(String[] args) throws IOException {
        try (Reader reader = new InputStreamReader(
                new FileInputStream("data.csv"), StandardCharsets.UTF_8);
             CSVParser parser = CSVFormat.DEFAULT
                     .withFirstRecordAsHeader() // 第一行作標題
                     .withTrim()                // 自動 trim
                     .parse(reader)) {

            for (CSVRecord record : parser) {
                String name = record.get("name");
                String age = record.get("age");
                String birthday = record.get("birthday");
                System.out.printf("%s | %s | %s%n", name, age, birthday);
            }
        }
    }
}

2. OpenCSV —— 功能豐富、支援映射到 Java Bean

<dependency>
    <groupId>com.opencsv</groupId>
    <artifactId>opencsv</artifactId>
    <version>5.7.1</version>
</dependency>
import com.opencsv.bean.*;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;

public class OpenCsvDemo {
    // 對應 CSV 欄位的 Java Bean
    public static class Person {
        @CsvBindByName private String name;
        @CsvBindByName private int age;
        @CsvBindByName private String birthday;
        // getter/setter/toString 省略
    }

    public static void main(String[] args) throws IOException {
        try (Reader reader = new InputStreamReader(
                new FileInputStream("data.csv"), StandardCharsets.UTF_8)) {

            CsvToBean csvToBean = new CsvToBeanBuilder(reader)
                    .withType(Person.class)
                    .withIgnoreLeadingWhiteSpace(true)
                    .build();

            for (Person p : csvToBean) {
                System.out.println(p);
            }
        }
    }
}

3. Jackson Dataformat CSV —— 企業級、流式、高效能

<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-csv</artifactId>
    <version>2.15.2</version>
</dependency>
import com.fasterxml.jackson.dataformat.csv.*;
import com.fasterxml.jackson.databind.*;
import java.io.*;
import java.util.*;

public class JacksonCsvDemo {
    public static class Person {
        public String name;
        public int age;
        public String birthday;
    }

    public static void main(String[] args) throws IOException {
        CsvMapper mapper = new CsvMapper();
        CsvSchema schema = mapper.schemaFor(Person.class).withHeader();

        try (MappingIterator it = mapper.readerFor(Person.class)
                .with(schema)
                .readValues(new File("data.csv"))) {

            while (it.hasNext()) {
                System.out.println(it.next());
            }
        }
    }
}

庫選型決策表

場景推薦理由
輕量、零依賴傾向、標準 RFC 4180Apache Commons CSVAPI 簡潔、無第三方依賴、Spring 內建
需映射到 Bean、自訂轉換器、註解驅動OpenCSV功能最全、Bean 映射成熟、社群活躍
高效能、大檔案流式、已用 Jackson 生態Jackson CSV零拷貝流式、與 JSON 共用 ObjectMapper 配置
Android / 極度受限環境手工解析 + 狀態機無庫體積,但需自行處理邊界案例

編碼與 BOM 處理

  • **預設 UTF-8**,顯式指定 StandardCharsets.UTF_8。
  • Excel 匯出的 CSV 可能帶 **UTF-8 BOM (EF BB BF)**,Commons CSV/Jackson 自動處理;手工讀取需跳過前 3 位元組。
  • 中文舊系統可能為 **Big5/GBK**,對應 Charset.forName("Big5") / StandardCharsets.ISO_8859_1(需確認實際編碼)。

大檔案流式處理記憶體安全

// 所有主流庫均支援 Iterator/Stream 流式讀取,不載入整檔到記憶體
try (CSVParser parser = CSVFormat.DEFAULT.parse(reader)) {
    for (CSVRecord record : parser) { // 一行一行處理
        process(record);
    }
}
// 或 Java 8 Stream
parser.stream().forEach(this::process);

總結

  • **手工 split(",") 僅限教學/極簡 CSV**,生產環境**必須使用成熟庫**。
  • **Apache Commons CSV** 為首選:輕量、標準、Spring 專案零額外依賴。
  • **OpenCSV** 適合複雜 Bean 映射需求。
  • **Jackson CSV** 適合高效能、大檔案、已用 Jackson 體系專案。
  • 統一使用 **UTF-8**、**顯式指定編碼**、**流式處理大檔案**、**Bean 映射減少手工索引錯誤**。

避坑指南:CSV 看起來像文字,實則是**有結構的資料**。任何「我自己寫個 split 就好」的念頭,都是技術債的開始。引入一個 100KB 的庫,換來的是 RFC 4180 完整相容與無數個深夜的安睡。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved