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

Java Iterator遍历CSV时跳过半数元素问题求助

问题原因分析及解决方案

根据你描述的现象(每3行仅读取1行,最终HashMap数据量减半)和提供的代码、CSV样本,我整理出几个最可能的原因及对应的解决办法:

1. CSV解析器误将多行合并为一行(核心嫌疑)

你的调试日志显示每次迭代跳过2行,直接读取第3行,这大概率是CSV解析时未正确处理字段中的特殊字符导致多行被合并成一行。比如:

  • 某些字段(如Country或Language)包含未被引号包裹的换行符、逗号等;
  • Adobe的Csv类默认配置未适配你的CSV格式(比如引号字符、分隔符设置错误)。

解决办法:

a. 验证CSV解析结果

在循环中打印每一行的解析内容,确认是否存在多行合并的情况:

while (rowIterator.hasNext()) {
    String[] row = rowIterator.next();
    System.out.println("当前解析行的字段数:" + row.length);
    // 打印所有字段,看是否包含多行内容
    for (int i = 0; i < row.length; i++) {
        System.out.printf("row[%d] = %s%n", i, row[i]);
    }
}

如果某一行的字段数远大于7(你的CSV表头有7列),说明确实存在多行合并的问题。

b. 显式配置Csv类的解析规则

默认的Csv实例可能没有启用引号解析,导致无法识别包含特殊字符的字段。显式配置引号和分隔符:

Csv csv = new Csv();
csv.setQuote('"'); // 设置CSV字段的引号包裹字符
csv.setSeparator(','); // 明确指定分隔符为逗号
csv.setTrimValues(true); // 可选:去除字段前后的空格

2. HashMap键重复导致数据被覆盖

你的键生成逻辑是locale.toLowerCase() + "_" + country.toLowerCase(),其中country取自row[1](ISO639-2 Country Code),locale取自row[4](ISO639-2 Lang)。如果CSV中存在相同国家代码+相同语言代码的多行数据,HashMap.put()会直接覆盖旧值,导致最终数据量远小于总行数。

解决办法:

a. 检查键的唯一性

在put前打印重复键的日志,确认是否存在覆盖:

String key = locale.toLowerCase() + "_" + country.toLowerCase();
if (localeToFormat.containsKey(key)) {
    System.out.printf("发现重复键:%s,旧值:%s,新值:%s%n", key, localeToFormat.get(key), dateFormat);
}
localeToFormat.put(key, dateFormat);

如果存在大量重复键,说明你需要调整键的生成规则(比如加入row[2]国家名称,或者使用更唯一的组合),或者改用HashMap<String, List<String>>存储多个值。

b. 修正字段索引错误(可选)

看你的CSV表头,row[2]才是实际的Country名称(比如"Albania"),而你当前用的row[1]是ISO639-2国家代码(比如"AL")。如果这不是你的预期,可能导致键的逻辑错误,建议修正索引:

String country = row[2]; // 改用实际国家名称
String locale = row[4];
String dateFormat = row[6];

3. 未跳过表头导致第一条数据错误

你的调试提到“表头已被跳过”,但代码中没有任何跳过表头的逻辑。如果Csv.read()默认不跳过表头,那么第一行(表头)会被当成数据行存入HashMap,同时占用一个条目。

解决办法:

在循环前显式跳过表头:

Iterator<String[]> rowIterator = csv.read(is, StandardCharsets.UTF_8.name());
// 跳过表头行
if (rowIterator.hasNext()) {
    rowIterator.next();
}
// 开始处理数据行
while (rowIterator.hasNext()) {
    // ... 你的逻辑
}

4. Adobe Csv.read()方法的代码错误

你提供的AdobeCsv.read()方法存在变量遮蔽问题:

InputStream in = new BufferedInputStream(in, 4096); // 局部变量in遮蔽了参数in

这行代码会导致构造BufferedInputStream时使用未初始化的局部变量in,引发NullPointerException或流读取异常。如果这是实际运行的代码,必须修复:

public Iterator<String[]> read(InputStream in, String charset) throws IOException {
    if (charset == null) {
        charset = System.getProperty("file.encoding");
    }
    // 重命名局部变量,避免遮蔽参数
    BufferedInputStream bufferedIn = new BufferedInputStream(in, 4096);
    this.input = new InputStreamReader(bufferedIn, charset);
    return this.read();
}

额外排查步骤

  1. 验证流的完整性:将CSV流写入字节数组,检查长度是否与文件实际大小一致,确认流没有被提前截断:
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = is.read(buffer)) != -1) {
    baos.write(buffer, 0, bytesRead);
}
byte[] csvContent = baos.toByteArray();
System.out.println("CSV文件字节长度:" + csvContent.length);
// 使用字节数组重新构建流进行解析
Iterator<String[]> rowIterator = csv.read(new ByteArrayInputStream(csvContent), StandardCharsets.UTF_8.name());
  1. 检查换行符兼容性:虽然你说换行符正确,但可以尝试将CSV文件转换为LF(Unix)格式,避免Windows CRLF格式可能带来的解析问题。

内容的提问来源于stack exchange,提问作者Jorel Amthor

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 09:57:28