
本文详解为何 totAmount 总是滞后一行写入文本文件,并提供完整、健壮的代码修正方案,核心在于正确初始化状态变量与重构累计逻辑。
本文详解为何 `totamount` 总是滞后一行写入文本文件,并提供完整、健壮的代码修正方案,核心在于正确初始化状态变量与重构累计逻辑。
问题根源在于循环逻辑中对首行处理的缺失:变量 prevBill 初始为空字符串(""),而第一行的 bill 值(如 "Bill-202300047")必然不等于空串,因此首次进入 else 分支——此时 totalAmount 仍为初始值 0.0,直接被格式化为 "0,0" 写入第一行;而该行实际应累加的金额(即第一笔账单的 amount)却被错误地赋给了下一轮的 totalAmount = amount,导致所有 totAmount 整体下移一行。
要彻底解决,必须确保每组相同 bill 的金额被正确累积,并在切换 bill 时输出上一组的汇总结果。关键改进点有三:
- 跳过首行前不输出:首次迭代不写入,仅初始化 prevBill 和 totalAmount;
- 统一累积逻辑:无论是否为新 bill,当前行 amount 都需纳入累计;
- 末尾补写最后一组:循环结束后,必须显式写出最后一组 bill 的汇总结果。
以下是修正后的核心逻辑(仅展示关键部分,已整合进完整流程):
// 初始化:跳过首行(i=1),但需先读取第1行以设置初始状态
Row firstRow = sheet.getRow(1);
if (firstRow == null) {
writer.close();
excelFile.close();
return;
}
// 初始化 prevBill 和 totalAmount 使用第一行数据
String bill = getCellValueAsString(row.getCell(5)); // 复用下方工具方法
prevBill = bill;
double amount = getCellValueAsDouble(row.getCell(14));
totalAmount = amount; // ✅ 首次即累加,而非留空
// 从第2行(i=2)开始主循环
for (int i = 2; i <= sheet.getLastRowNum(); i++) {
Row row = sheet.getRow(i);
if (row == null) continue;
// ... [同前:cl 过滤、bill/nomJournal/onlyNumb 等提取逻辑] ...
// ✅ 关键:当前行 amount 无条件加入累计
totalAmount += amount;
// 若 bill 变化,输出上一组汇总(即 prevBill 对应的 totalAmount)
if (!bill.equals(prevBill)) {
String totAmount = String.format(Locale.FRANCE, "%.1f", totalAmount).replace(".", ",");
String line = "\"" + nomJournal + "\";"
+ "\"" + year + "\";"
+ "\"" + year + "\";"
+ "\"" + month + "\";"
+ "\"" + onlyNumb + "\";"
+ "\"" + formattedDate + "\";"
+ "\"C\";"
+ "\"" + numTVA + "\";"
+ "\"" + formattedDate + "\";"
+ "\"" + totAmount + "\";"
+ "\"" + customer + "\";"
+ "\"" + matchNo + "\";"
+ "\"S\";";
writer.println(line);
// 重置:新 bill 的累计从当前行 amount 开始(非 0!)
totalAmount = amount;
prevBill = bill;
}
}
// ✅ 必须:循环结束后,输出最后一组 bill 的汇总
if (!prevBill.isEmpty()) {
String totAmount = String.format(Locale.FRANCE, "%.1f", totalAmount).replace(".", ",");
// 构造 line(注意:year/month/onlyNumb/formattedDate 等需基于最后一行有效值)
// 实际中建议将这些字段提取逻辑封装为方法,避免重复计算
String line = "\"" + nomJournal + "\";"
+ "\"" + year + "\";"
+ "\"" + year + "\";"
+ "\"" + month + "\";"
+ "\"" + onlyNumb + "\";"
+ "\"" + formattedDate + "\";"
+ "\"C\";"
+ "\"" + numTVA + "\";"
+ "\"" + formattedDate + "\";"
+ "\"" + totAmount + "\";"
+ "\"" + customer + "\";"
+ "\"" + matchNo + "\";"
+ "\"S\";";
writer.println(line);
}辅助方法建议(提升可读性与健壮性):
private static String getCellValueAsString(Cell cell) { if (cell == null) return ""; return cell.getCellType() == CellType.NUMERIC ? String.valueOf((long) cell.getNumericCellValue()) : cell.getStringCellValue().trim(); } private static double getCellValueAsDouble(Cell cell) { if (cell == null) return 0.0; return cell.getCellType() == CellType.NUMERIC ? cell.getNumericCellValue() : Double.parseDouble(cell.getStringCellValue().trim()); }
注意事项:
- SimpleDateFormat 非线程安全,若多线程调用需局部声明或改用 DateTimeFormatter(Java 8+);
- Excel 日期解析务必使用 DateUtil.getJavaDate(),避免 Double.parseDouble(closedDate) 直接转换引发异常;
- numTVA 截取逻辑中 substring(2) 前应校验长度,防止 StringIndexOutOfBoundsException;
- 生产环境建议添加 try-with-resources 管理 PrintWriter 和 FileInputStream,确保资源自动释放。
通过上述重构,totAmount 将严格对应其所属 bill 组的全部金额总和,且首行即为真实数据,彻底消除“下移一行”的逻辑缺陷。

















