
本文详解如何用 java 读取空格分隔的五列文本数据(商品描述、数量、重量、单价、关税码),按关税码分组汇总数量、重量和价格,并将聚合结果写入新文件。全程采用面向对象设计与 stream api,兼顾可读性与工程实践性。
本文详解如何用 java 读取空格分隔的五列文本数据(商品描述、数量、重量、单价、关税码),按关税码分组汇总数量、重量和价格,并将聚合结果写入新文件。全程采用面向对象设计与 stream api,兼顾可读性与工程实践性。
在实际业务处理中(如海关报关、库存汇总等场景),常需对结构化文本数据按关键字段(如关税码)进行聚合统计。本教程以一个典型需求为例:从 input.txt 文件读取商品数据(5 列,空格分隔),按关税码(tariff code) 合并同类项,累加数量、重量和价格,并将结果输出至 output.txt。
⚠️ 注意:原始示例中误将分组依据写作“description”(商品名),但题干明确要求 “rearrange in accordance with tariff code”(按关税码归类)。因此正确逻辑应以第 5 列 tariffCode 为分组键,而非商品名称(否则 pen 和 pencil 的重复关税码 9608101000/9609101000 将无法合并)。以下实现严格遵循题意。
✅ 核心设计思路
- 定义 WritingKit 类封装一行数据(含 getter/setter、解析构造器 valueOf()、聚合静态方法 combine())
- 使用 NumberFormat.getInstance(Locale.GERMAN) 正确解析逗号小数(如 "0,3" → 0.3)
- 利用 Files.lines() + Stream 链式处理:读取 → 分割 → 解析 → 收集为列表
- 使用 Collectors.groupingBy(WritingKit::getTariffCode, Collectors.reducing(...)) 按关税码聚合
- 输出格式化为制表符分隔(兼容 Excel 打开),保留两位小数
? 完整可运行代码
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.text.NumberFormat;
import java.text.ParsePosition;
import java.util.*;
import java.util.stream.Collectors;
// 数据模型:代表一行商品记录
class WritingKit {
private String description;
private int quantity;
private double weight;
private double price;
private String tariffCode;
private static final NumberFormat nf = NumberFormat.getNumberInstance(Locale.GERMAN);
public WritingKit(String description, int quantity, double weight, double price, String tariffCode) {
this.description = description;
this.quantity = quantity;
this.weight = weight;
this.price = price;
this.tariffCode = tariffCode;
}
// 从字符串数组解析一行(空格分割后调用)
public static WritingKit valueOf(String[] parts) {
if (parts.length items = Files.lines(Path.of(inputPath))
.filter(line -> !line.trim().isEmpty()) // 跳过空行
.map(line -> line.split("\s+")) // 用正则 "\s+" 处理多个空格
.map(WritingKit::valueOf)
.collect(Collectors.toList());
System.out.println("✅ 原始数据共 " + items.size() + " 行已加载");
// 2. 按 tariffCode 分组聚合(关键步骤)
Map<string writingkit> aggregated = items.stream()
.collect(Collectors.groupingBy(
WritingKit::getTariffCode,
Collectors.collectingAndThen(
Collectors.reducing(
null,
item -> item,
WritingKit::combine
),
Optional::get
)
));
// 3. 写入结果文件
List<string> outputLines = new ArrayList();
for (WritingKit wk : aggregated.values()) {
outputLines.add(wk.toCsv());
}
Files.write(Path.of(outputPath), outputLines);
System.out.println("✅ 聚合完成,共 " + aggregated.size() + " 个关税码,结果已写入: " + outputPath);
// 可选:控制台预览结果
System.out.println("
? 聚合结果预览:");
System.out.println("描述 数量 重量 价格 关税码");
aggregated.values().forEach(System.out::println);
} catch (IOException e) {
System.err.println("❌ 文件操作失败: " + e.getMessage());
e.printStackTrace();
} catch (Exception e) {
System.err.println("❌ 数据解析或计算异常: " + e.getMessage());
e.printStackTrace();
}
}
}</string></string>
? 关键注意事项
- 路径写法:Windows 下路径应使用双反斜杠 \ 或正斜杠 /,例如 "C:/data/input.txt";避免原始代码中的非法转义(如 "C: "Test...")。
- 空格分割健壮性:使用 split("\s+") 替代 split(" "),可正确处理多空格、制表符等空白字符。
- 小数点兼容性:Locale.GERMAN 支持 "," 作为小数分隔符(如 "0,3"),若输入为英文格式("0.3"),可改用 Locale.US 或直接 Double.parseDouble()。
- 内存安全:Files.lines() 返回流式 Stream,适合大文件;若需极致性能,可配合 BufferedReader 手动逐行处理。
- 错误处理:生产环境建议增加更细粒度的异常捕获(如 NumberFormatException 单独处理),并记录具体出错行号。
✨ 总结
本方案摒弃了易出错的二维字符数组(char[][])手动解析方式,转而采用领域模型驱动 + 函数式流处理,代码简洁、逻辑清晰、扩展性强。掌握此模式后,你可轻松适配类似需求:更换分组字段、增减聚合指标、切换输入/输出格式(CSV/JSON/数据库)等。记住:面向对象建模是 Java 工程化的基石,而 Stream API 是现代数据处理的利器——二者结合,让复杂任务变得优雅可控。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











