GitHub Actions(Linux)下CSV首元素显示'???"'的解决方法
问题排查与解决方案
问题根源
- 编码依赖系统默认值:
FileReader会使用系统默认编码读取文件,Linux与Windows的默认编码差异,导致UTF-8 BOM(字节顺序标记)被解析为乱码字符???"。 - BOM处理时机错误:现有代码在字符串层面处理BOM,但当编码不匹配时,BOM已经被转成乱码,
startsWith无法匹配标准BOM字符,导致处理逻辑完全失效。
修复方案
方案1:统一编码+输入流层面移除BOM
直接在文件读取阶段指定UTF-8编码,并手动跳过UTF-8 BOM(字节序列EF BB BF),从根源避免乱码生成:
private static void validateCsvHeaders(File file, List<String> csvContent, List<ExtentTest> extentLog) { try (FileInputStream fis = new FileInputStream(file); InputStreamReader isr = new InputStreamReader(fis, StandardCharsets.UTF_8)) { // 检查并跳过UTF-8 BOM fis.mark(3); byte[] bom = new byte[3]; if (!(fis.read(bom) == 3 && bom[0] == (byte)0xEF && bom[1] == (byte)0xBB && bom[2] == (byte)0xBF)) { fis.reset(); // 没有BOM则重置读取位置 } try (CSVReader csvReader = new CSVReader(isr)) { List<String> header = Arrays.asList(csvReader.readNext()); for (int i = 0; i < header.size(); i++) { int finalI = i; String cleanedHeader = header.get(i).trim(); if (cleanedHeader.contains(csvContent.get(i))) { extentLog.stream().forEach(Exlog -> Exlog.log(Status.PASS, header.get(finalI) + " is present in CSV file: " + csvContent.get(finalI))); } else { extentLog.stream().forEach(Exlog -> Exlog.log(Status.INFO, "Expected header is " + csvContent.get(i) + " and actual header is " + cleanedHeader)); extentLog.stream().forEach(Exlog -> Exlog.log(Status.FAIL, cleanedHeader + " is not present in CSV file, expected: " + csvContent.get(i))); } } } } catch (IOException | CsvValidationException e) { extentLog.stream().forEach(Exlog -> Exlog.log(Status.FAIL, "Error reading CSV file " + file.getName() + ": " + e.getMessage())); } }
此时可以直接删除removeBOMFromCSV方法,因为BOM已经在输入流阶段被处理。
方案2:使用支持自动处理BOM的CSV库(推荐)
如果使用OpenCSV 5.x及以上版本,可直接通过配置启用BOM自动处理:
private static void validateCsvHeaders(File file, List<String> csvContent, List<ExtentTest> extentLog) { try (FileReader fr = new FileReader(file, StandardCharsets.UTF_8)) { CSVReader csvReader = new CSVReaderBuilder(fr) .withBomHandler(BomHandler.UTF_8) // 自动识别并跳过UTF-8 BOM .build(); List<String> header = Arrays.asList(csvReader.readNext()); // 后续校验逻辑同方案1 } catch (IOException | CsvValidationException e) { extentLog.stream().forEach(Exlog -> Exlog.log(Status.FAIL, "Error reading CSV file " + file.getName() + ": " + e.getMessage())); } }
若改用Apache Commons CSV,它会自动识别并跳过BOM,代码更简洁:
private static void validateCsvHeaders(File file, List<String> csvContent, List<ExtentTest> extentLog) { try (Reader reader = Files.newBufferedReader(file.toPath(), StandardCharsets.UTF_8)) { CSVParser parser = CSVParser.parse(reader, CSVFormat.DEFAULT.withHeader()); Map<String, String> headerMap = parser.getHeaderMap(); for (String expectedHeader : csvContent) { if (headerMap.containsKey(expectedHeader)) { extentLog.stream().forEach(Exlog -> Exlog.log(Status.PASS, expectedHeader + " is present in CSV file")); } else { extentLog.stream().forEach(Exlog -> Exlog.log(Status.FAIL, expectedHeader + " is not present in CSV file")); } } } catch (IOException e) { extentLog.stream().forEach(Exlog -> Exlog.log(Status.FAIL, "Error reading CSV file " + file.getName() + ": " + e.getMessage())); } }
额外注意事项
- 建议将CSV文件保存为UTF-8无BOM格式,从源头避免BOM相关问题。
- 所有文件读取操作统一指定UTF-8编码,彻底消除系统编码差异带来的影响。
内容的提问来源于stack exchange,提问作者Ashutosh jha
相关产品推荐
相关产品推荐

