CSV 看似簡單,實則暗藏特殊字符處理陷阱:引號包裹、嵌入換行、雙引號轉義、編碼混雜。手工 `split(",")` 註定翻車。本文實戰演示專業庫 **uniVocity-parsers** (現代首選) 與 **Apache Commons CSV**,還原原始數據,配合參數化查詢安全入庫。
"張三","25","北京" // 普通字段
"李四,工程師","30","上海" // 字段含逗號 → 必須雙引號包裹
"王五","他說:\"你好\"","28" // 字段含雙引號 → 雙引號轉義為兩個雙引號
"趙六","第一行
第二行","35" // 字段含換行 → 雙引號包裹跨行
"錢七",,"40" // 空字段
手工解析需狀態機處理:引號內逗號不分隔、雙雙引號轉義、跨行合併、編碼識別... 輪子造不圓,專業庫已完美解決。
com.univocity
univocity-parsers
2.9.1
public List parseCsvRaw(Path filePath, Charset charset) throws IOException {
CsvParserSettings settings = new CsvParserSettings();
settings.setLineSeparatorDetectionEnabled(true); // 自動識別 \n/\r\n/\r
settings.setDelimiterDetectionEnabled(true); // 自動檢測分隔符 (逗號/分號/製表符)
settings.setMaxCharsPerColumn(-1); // 無長度限制
settings.setMaxColumns(-1);
settings.setEmptyValue(""); // 空字段返回空串而非 null
settings.setNullValue(""); // null 字段返回空串
settings.setKeepEscapeSequences(false); // 移除轉義序列,還原原始內容
settings.setIgnoreLeadingWhitespaces(true);
settings.setIgnoreTrailingWhitespaces(true);
settings.setHeaderExtractionEnabled(false); // 不提取表頭,全作數據行
// 關鍵:保持原始字符串,不做任何類型轉換、日期解析、修剪
settings.setInputBufferSize(1024 * 1024); // 1MB 緩衝區
try (CsvParser parser = new CsvParser(settings);
Reader reader = Files.newBufferedReader(filePath, charset)) {
return parser.parseAll(reader); // 一次性讀全量 (中小文件)
// 大文件流式處理見下文
}
}
// 流式處理大文件 (避免 OOM)
public void streamProcessLargeCsv(Path filePath, Charset charset, Consumer rowHandler) throws IOException {
CsvParserSettings settings = new CsvParserSettings();
settings.setLineSeparatorDetectionEnabled(true);
settings.setDelimiterDetectionEnabled(true);
settings.setEmptyValue("");
settings.setNullValue("");
settings.setKeepEscapeSequences(false);
try (CsvParser parser = new CsvParser(settings);
Reader reader = Files.newBufferedReader(filePath, charset)) {
parser.beginParsing(reader);
String[] row;
while ((row = parser.parseNext()) != null) {
rowHandler.accept(row); // 逐行處理:入庫、轉換、過濾、聚合
}
parser.stopParsing();
}
}
@Parse
public class UserCsvDto {
@Trim
@LowerCase
@NotNull
private String username;
@Convert(converter = LocalDateConverter.class, pattern = "yyyy-MM-dd")
private LocalDate birthday;
@Parsed(index = 2) // 按列索引映射
private String email;
// getter/setter
}
// 解析映射
BeanListProcessor processor = new BeanListProcessor<>(UserCsvDto.class);
CsvParserSettings settings = new CsvParserSettings();
settings.setProcessor(processor);
settings.setHeaderExtractionEnabled(true); // 第一行作表頭,按列名匹配
new CsvParser(settings).parse(new FileReader("users.csv"));
List users = processor.getBeans();
org.apache.commons
commons-csv
1.10.0
public List parseWithCommonsCsv(Path filePath, Charset charset) throws IOException {
CSVFormat format = CSVFormat.DEFAULT
.withDelimiter(',')
.withQuote('"')
.withEscape('"') // 雙引號轉義
.withIgnoreEmptyLines(false)
.withTrim(false) // 關鍵:不修剪空白,保持原始
.withNullString("") // null 字段返回空串
.withRecordSeparator("\n"); // 或自動檢測
try (Reader reader = Files.newBufferedReader(filePath, charset);
CSVParser parser = format.parse(reader)) {
return parser.getRecords().stream()
.map(record -> record.values().toArray(new String[0]))
.collect(Collectors.toList());
}
}
| 維度 | uniVocity-parsers | Apache Commons CSV | OpenCSV |
|---|---|---|---|
| 性能 (大文件) | ⭐⭐⭐⭐⭐ 極快 | ⭐⭐⭐ 良好 | ⭐⭐ 較慢 |
| 功能豐富度 | ⭐⭐⭐⭐⭐ Bean映射、類型轉換、固定寬度、多字符分隔符 | ⭐⭐⭐ 基礎完備 | ⭐⭐⭐ 基礎+Bean映射 |
| API 現代度 | ⭐⭐⭐⭐ Fluent、Processor 模式 | ⭐⭐⭐ 標準 Iterator | ⭐⭐ 較舊 |
| 依賴體積 | ~500KB | ~150KB | ~200KB |
| Spring Boot 整合 | 手動引入 | 常隨 spring-boot-starter 伝遞 | 手動引入 |
| 推薦場景 | 高性能、複雜 CSV、Bean 映射、大文件流式 | 輕量、簡單解析、已有依賴 | 歷史項目維護 |
// 1. 解析獲取原始字符串數組 (無任何轉義殘留)
String[] rawFields = parseCsvRaw(path, StandardCharsets.UTF_8).get(rowIndex);
// 2. 業務層驗證/轉換 (而非解析層)
User user = User.fromCsv(rawFields); // 領域工廠方法,集中驗證邏輯
// 3. 參數化查詢入庫 (JDBC PreparedStatement / MyBatis / JPA / JdbcTemplate)
// 完全委託驅動程序處理特殊字符轉義、SQL 注入防護
String sql = "INSERT INTO users(name, bio, email) VALUES (?, ?, ?)";
jdbcTemplate.update(sql, user.getName(), user.getBio(), user.getEmail());
// 無需手工 replace("'", "''") 等操作!
核心原則:解析層只負責**結構還原** (行/列分割、引號/轉義處理),**不做業務清洗**;業務層做**語義驗證/轉換**;持久層用**參數化綁定**自動處理特殊字符。三層職責分離,安全、可測、可維護。
CSV 解析**不要手寫**,選 **uniVocity-parsers** (性能/功能雙強) 或 **Commons CSV** (輕量/零額外依賴)。核心配置:`keepEscapeSequences=false`、`trim=false`、`emptyValue=""` 還原原始字符串。下游用參數化查詢入庫,徹底斷開「特殊字符導致 SQL 錯誤/注入」鏈路。專業工具專業用,業務邏輯乾淨純粹。