
本文介绍如何用 java 读取空格分隔的五列文本数据(商品描述、数量、重量、价格、关税编码),按关税编码合并同类项,累加数量、重量和价格,并将聚合结果写入新文件。全程采用面向对象设计与 stream api,兼顾可读性与健壮性。
本文介绍如何用 java 读取空格分隔的五列文本数据(商品描述、数量、重量、价格、关税编码),按关税编码合并同类项,累加数量、重量和价格,并将聚合结果写入新文件。全程采用面向对象设计与 stream api,兼顾可读性与健壮性。
一、核心思路:面向对象建模 + 流式聚合
直接操作二维字符数组(如 char[][])不仅难以处理数值计算、小数点格式(如 "0,3")、空格分割逻辑,还极易引发索引越界或类型转换异常。更合理的方式是:
- 定义业务实体类(WritingKit)封装每行语义:描述、数量(int)、重量/价格(double,需支持逗号小数)、关税编码(String);
- 统一解析逻辑:使用 NumberFormat(指定 Locale.GERMAN)正确解析 "0,3" 类格式;
- 流式处理:用 Files.lines() 按行读取 → split(" ") 分割字段 → WritingKit.valueOf() 构造对象 → Collectors.groupingBy(..., reducing()) 按关税编码聚合。
✅ 注意:原题示例中“按 tariff code 合并”,但样例输出实为按商品描述(如 pen, pencil)合并(因相同描述对应相同 tariff code)。本教程严格遵循样例逻辑——即以 description 为分组键;若需改为 tariffCode,仅需将 groupingBy(WritingKit::getDescription) 替换为 groupingBy(WritingKit::getTariffCode)。
二、关键代码实现
1. 业务实体类 WritingKit
import java.text.NumberFormat;
import java.text.ParsePosition;
import java.util.Locale;
public class WritingKit {
private String description;
private int quantity;
private double weight;
private double price;
private String tariffCode;
// 使用德语 Locale 解析逗号小数(如 "0,3")
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[] attrs) {
return new WritingKit(
attrs[0].trim(),
Integer.parseInt(attrs[1].trim()),
nf.parse(attrs[2].trim(), new ParsePosition(0)).doubleValue(),
nf.parse(attrs[3].trim(), new ParsePosition(0)).doubleValue(),
attrs[4].trim()
);
}
// 合并两个同类商品(用于 reduce)
public static WritingKit combine(WritingKit a, WritingKit b) {
return new WritingKit(
a.description, // 保持首个描述(实际分组后描述一致)
a.quantity + b.quantity,
a.weight + b.weight,
a.price + b.price,
a.tariffCode
);
}
// 格式化为制表符分隔的字符串(便于写入文件)
public String toCsv() {
return String.format("%s %d %.2f %.2f %s",
description, quantity, weight, price, tariffCode);
}
// Getter 方法(略,实际使用中需补全)
public String getDescription() { return description; }
public int getQuantity() { return quantity; }
public double getWeight() { return weight; }
public double getPrice() { return price; }
public String getTariffCode() { return tariffCode; }
}
2. 主程序:读取、聚合、写入
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
public class WritingItems {
public static void main(String[] args) {
if (args.length == 0) {
System.err.println("Usage: java WritingItems <input-file-path>");
return;
}
String inputPath = args[0];
String outputPath = inputPath.replace(".txt", "_output.txt");
try {
// 1. 读取所有行 → 解析为 WritingKit 列表
List<writingkit> items = Files.lines(Paths.get(inputPath))
.map(line -> line.split("\s+")) // 使用正则 "\s+" 处理多个空格
.map(WritingKit::valueOf)
.collect(Collectors.toList());
// 2. 按 description 分组并累加(quantity/weight/price)
Map<string optional>> grouped = items.stream()
.collect(Collectors.groupingBy(
WritingKit::getDescription,
Collectors.reducing(WritingKit::combine)
));
// 3. 写入结果文件
List<string> outputLines = grouped.values().stream()
.map(opt -> opt.orElseThrow(() -> new RuntimeException("Empty group")))
.map(WritingKit::toCsv)
.toList();
Files.write(Paths.get(outputPath), outputLines);
System.out.printf("✅ 成功处理 %d 行数据,结果已写入:%s%n", items.size(), outputPath);
} catch (IOException e) {
System.err.println("❌ 文件操作失败:" + e.getMessage());
e.printStackTrace();
} catch (NumberFormatException e) {
System.err.println("❌ 数据格式错误(检查小数逗号/非数字字符):" + e.getMessage());
}
}
}</string></string></writingkit></input-file-path>
三、使用说明与注意事项
-
输入文件路径:运行时传入绝对或相对路径,例如:
java WritingItems "C:\Test for java input.txt" -
编码兼容性:代码默认使用系统默认编码读取文件。若文件为 UTF-8 且含中文,建议在 Files.lines() 中显式指定:
Files.lines(Paths.get(inputPath), StandardCharsets.UTF_8) - 空格分割健壮性:使用 split("\s+") 替代 split(" "),可正确处理多空格、制表符等空白字符。
- 错误处理:已添加 IOException 和 NumberFormatException 捕获,便于定位文件不存在或数据格式问题。
- 输出格式:结果以制表符 分隔,与输入对齐;数值保留两位小数(如 7.54),符合样例要求。
通过此方案,你将摆脱低层数组操作的复杂性,以清晰、可维护、可扩展的方式完成数据聚合任务——这正是 Java 面向对象与函数式编程结合的最佳实践。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











