17370845950

OpenCSV 读取 CSV 文件首列为空的解决方案(BOM 字符导致)

opencsv 在 spring boot 中解析 csv 时首列值为 null,通常是由 utf-8 bom(字节顺序标记 \ufeff)引起;文件开头隐藏的 bom 被误读为第一列字段名前缀,导致列名匹配失败。

在使用 OpenCSV 的 CsvToBeanBuilder 解析带表头的 CSV 文件时,若首列(如 "Departure")始终返回 null,而其余列正常,极大概率是 CSV 文件以 UTF-8 with BOM 编码保存——Windows 系统下常见编辑器(如记事本、部分版本的 IntelliJ IDEA)默认添加不可见的 BOM 字符 \uFEFF 到文件开头。该字符会“污染”首列字段名,使 OpenCSV 实际匹配的列为 "\uFEFFDeparture",而非预期的 "Departure",从而导致绑定失败。

✅ 正确做法:在构造 Reader 时显式剥离 BOM
推荐使用 Apache Commons IO 提供的 BOMInputStream(需引入依赖):



    commons-io
    commons-io
    2.11.0

然后修改读取逻辑如下:

import org.apache.commons.io.input.BOMInputStream;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;

// ...

InputStream input = new FileInputStream(path);
Reader reader = new InputStreamReader(new BOMInputStream(input), StandardCharsets.UTF_8);

CsvToBean csvToBean = new CsvToBeanBuilder<>(reader)
        .withType(CsvBikeTrips.class)
        .withIgnoreLeadingWhiteSpace(true) // 建议启用,增强鲁棒性
        .withSkipLines(0) // 若含 BOM,无需额外跳过行
        .build();

List trips = csvToBean.parse();

⚠️ 注意事项:

  • 不要手动用 Files.newBufferedReader(Paths.get(path)),它无法自动处理 BOM;
  • 避免使用 new InputStreamReader(new FileInputStream(...), "UTF-8")(未处理 BOM);
  • withIgnoreLeadingWhiteSpace(false) 可保留,但建议设为 true 以兼容字段前后空格;
  • 若无法引入 commons-io,可手动跳过 BOM(不推荐):
    InputStream is = new FileInputStream(path);
    if (is.available() >= 3) {
        byte[] bom = new byte[3];
        is.read(bom);
        if (!(bom[0] == (byte)0xEF && bom[1] == (byte)0xBB && bom[2] == (byte)0xBF)) {
            is = new ByteArrayInputStream(bom); // 重置流(需更严谨实现)
        }
    }

? 验证与调试技巧:

  • 用十六进制编辑器(如 HxD)或命令行 xxd -g1 file.csv | head 检查文件开头是否含 ef bb bf;
  • 在代码中打印实际读取的首行字段名:
    System.out.println("First line: " + reader.readLine()); // 查看是否含 \uFEFF

总结:OpenCSV 本身不内置 BOM 处理机制,必须在 Reader 层预处理。使用 BOMInputStream 是最简洁、可靠且符合 Spring Boot 工程规范的解决方案。