Java中用Univocity解析6GB大CSV并生成指定格式输出的方法问询
解决方案:用Univocity流式解析并分组合并CSV输出
针对你处理6GB大型CSV并生成特定格式输出的需求,我们可以利用Univocity的流式解析特性,在遍历每行数据时维护当前分组的状态,避免加载整个文件到内存,完美适配大文件场景。
核心思路
你的CSV数据是按以"A"开头的行作为分组头部,后续以"null"开头的行属于当前分组。我们需要:
- 跟踪当前分组的元数据(分组标识、第四列固定值)
- 收集分组内所有行的第二列值(用分号分隔)
- 收集分组内所有行的第三列值(用逗号分隔)
- 每当遇到新的分组头部时,先输出上一个分组的结果,再初始化新分组;遍历结束后输出最后一个分组
完整实现代码
import com.univocity.parsers.csv.CsvParser; import com.univocity.parsers.csv.CsvParserSettings; import java.io.File; import java.util.ArrayList; import java.util.List; public class CsvGroupParser { public static void main(String[] args) { CsvParserSettings settings = new CsvParserSettings(); settings.getFormat().setLineSeparator("\n"); // 可以根据需要开启更多优化,比如跳过空行等 settings.setSkipEmptyLines(true); CsvParser parser = new CsvParser(settings); File f = new File("test.csv"); // 维护当前分组的状态 String currentGroupId = null; String currentGroupLastValue = null; List<String> column2Values = new ArrayList<>(); List<String> column3Values = new ArrayList<>(); try { parser.beginParsing(f, "UTF-8"); String[] row; while ((row = parser.parseNext()) != null) { // 处理分组头部行(以"A"开头) if ("A".equals(row[0])) { // 如果已有未输出的分组,先输出 if (currentGroupId != null) { printGroup(currentGroupId, column2Values, column3Values, currentGroupLastValue); } // 初始化新分组 currentGroupId = row[0]; currentGroupLastValue = row[3]; column2Values.clear(); column3Values.clear(); // 把当前头部行的第二、第三列加入集合 column2Values.add(row[1]); column3Values.add(row[2]); } else if ("null".equals(row[0])) { // 处理分组内的行,收集第二、第三列 column2Values.add(row[1]); column3Values.add(row[2]); } } // 遍历结束后,输出最后一个分组 if (currentGroupId != null) { printGroup(currentGroupId, column2Values, column3Values, currentGroupLastValue); } } finally { parser.stopParsing(); // 确保关闭解析器,释放资源 } } // 格式化并输出单个分组的结果 private static void printGroup(String groupId, List<String> col2, List<String> col3, String lastVal) { // 用分号拼接第二列集合 String col2Str = "[" + String.join(";", col2) + "]"; // 用逗号拼接第三列集合 String col3Str = "[" + String.join(",", col3) + "]"; // 按期望格式输出 System.out.printf("%s,%s,%s,%s%n", groupId, col2Str, col3Str, lastVal); } }
代码说明
- 流式解析:Univocity的
parseNext()方法逐行读取数据,不会加载整个大文件到内存,适合6GB级别的文件处理。 - 分组状态管理:用变量跟踪当前分组的ID和第四列固定值,用
List收集分组内的第二、第三列数据,每个分组处理完成后就清空列表,内存占用极低。 - 格式化输出:用
String.join()替代手动replaceAll(),更简洁高效,避免正则表达式的性能损耗。 - 资源释放:在
finally块中调用stopParsing(),确保解析器资源被正确释放。
测试结果
针对你提供的test.csv,运行代码后会输出:
A,[10;11;30;44;16],[2,A1,A23,A34,A67],3 A,[20;41;60;74;86],[5,A100,A56,A34,A56],6
完全符合你的期望输出。
额外优化建议
- 如果CSV中可能存在其他非"A"非"null"开头的行,可以添加逻辑跳过或处理这些行。
- 可以开启Univocity的并行解析(
settings.setNumberOfThreads(...))进一步提升大文件的解析速度,但需要注意线程安全(不过这里每个分组是顺序处理的,并行解析主要是加速行读取)。
内容的提问来源于stack exchange,提问作者user2419320
相关产品推荐
相关产品推荐

