Java场景下如何正确处理UTF-8编码的CSV文件?
问题本质
Excel导出的「CSV UTF-8」文件开头会添加UTF-8字节顺序标记(BOM)(十六进制0xEFBBBF),但Java标准FileReader/BufferedReader不会自动识别并跳过这个BOM。这会导致读取到的第一行首字符实际是Unicode码点U+FEFF(零宽无间断空格),而非预期的#。另外,FileReader.getEncoding()返回结果不可靠——它仅返回平台默认编码(你的环境中是UTF-8),无法区分带BOM的UTF-8文件和纯ASCII文件。
正统解决方案
1. 手动检测并跳过BOM
在读取文件前,先检查输入流开头的BOM字节,若存在则跳过:
File f = fc.showOpenDialog(this.getMyWindow()); FileInputStream fis = new FileInputStream(f); // 检测并跳过UTF-8 BOM byte[] bom = new byte[3]; if (fis.read(bom) == 3 && bom[0] == (byte)0xEF && bom[1] == (byte)0xBB && bom[2] == (byte)0xBF) { // BOM已跳过 } // 用UTF-8编码包装处理后的输入流 BufferedReader br = new BufferedReader(new InputStreamReader(fis, StandardCharsets.UTF_8)); int rowCnt = 0; String line; while ((line = br.readLine()) != null) { rowCnt++; if (line.isEmpty()) continue; String[] row = line.split("[,|]|\\t"); String cell = row[0].strip(); if (cell.isEmpty()) continue; // 现在首字符应为正常的#,无需再检测字节长度 if (cell.charAt(0) == '#') { // 处理注释行逻辑 } else { // 处理数据行逻辑 } } br.close();
2. 使用Apache Commons IO的BOMInputStream
若可引入第三方库,BOMInputStream会自动处理各种编码的BOM:
// 需先引入Apache Commons IO依赖 File f = fc.showOpenDialog(this.getMyWindow()); BOMInputStream bomIn = new BOMInputStream(new FileInputStream(f), ByteOrderMark.UTF_8); BufferedReader br = new BufferedReader(new InputStreamReader(bomIn, StandardCharsets.UTF_8)); // 后续读取逻辑同上
3. 逐行移除BOM字符
若无法修改输入流初始化逻辑,可在读取每行后手动移除开头的U+FEFF字符:
String line; while ((line = br.readLine()) != null) { // 移除开头的BOM字符 if (!line.isEmpty() && line.charAt(0) == '\uFEFF') { line = line.substring(1); } // 后续处理逻辑 }
关键知识点
- UTF-8 BOM:并非UTF-8标准强制要求,但Excel等工具会添加它来标识文件编码。Java标准库默认不处理,需手动干预。
- 指定编码读取:始终用
InputStreamReader并明确指定StandardCharsets.UTF_8,而非依赖FileReader的平台默认编码,避免编码不一致问题。 - 避免依赖
String.getBytes():该方法会使用平台默认编码转换,结果不可靠,直接通过字符码点(如charAt(0) == '\uFEFF')检测BOM更准确。
内容的提问来源于stack exchange,提问作者Mark D Henning
相关产品推荐
相关产品推荐

