You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

GitHub Actions(Linux)下CSV首元素显示'???"'的解决方法

问题排查与解决方案

问题根源

  1. 编码依赖系统默认值:FileReader会使用系统默认编码读取文件,Linux与Windows的默认编码差异,导致UTF-8 BOM(字节顺序标记)被解析为乱码字符???"。
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 11:50:14