使用Apache POI复制两个Excel工作表至新表出现部分输出异常
问题分析与解决方案
我一眼就看到问题出在哪了——你复制第二个工作表的时候,行索引从0开始,直接调用outputSheet.createRow(cri)会把第一个工作表已经复制好的行给覆盖掉!因为POI的createRow方法如果目标行已经存在,会直接替换掉原有的行对象,之前第一个表复制的内容自然就没了。
除此之外,你的代码还有几个小问题:
- 直接用
cell.toString()获取单元格内容会导致格式错误(比如日期变成数字串、数值变成科学计数法) - 没有处理空行和空单元格的情况
- 迭代器没有使用泛型,代码不够严谨
修复步骤
1. 修正行索引的连续性
复制第二个工作表时,应该从第一个工作表的最后一行的下一行开始,而不是从0重新计数。我们可以用第一个表复制完成后的行索引作为第二个表的起始行索引,这样两个表的内容就会上下拼接,不会覆盖。
2. 优化单元格值的获取逻辑
根据单元格的类型(数值、字符串、日期等)来获取对应的值,避免toString()带来的格式问题。
3. 处理空行和空单元格
确保即使原工作表中有空行或者空单元格,也能正确复制到目标表中。
修正后的完整代码
import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFSheet; public class ExcelSheetCopier { public static void copySheet(XSSFSheet inputSheet1, XSSFSheet outputSheet, XSSFSheet inputSheet2) { // 复制第一个工作表,返回下一个可用的行索引 int nextRowIndex = copySingleSheet(inputSheet1, outputSheet, 0); // 复制第二个工作表,从第一个表的最后一行下一行开始 copySingleSheet(inputSheet2, outputSheet, nextRowIndex); } // 抽取通用的单表复制方法,复用逻辑 private static int copySingleSheet(Sheet inputSheet, Sheet outputSheet, int startRowIndex) { int lastRowNum = inputSheet.getLastRowNum(); // 遍历输入表的每一行 for (int i = 0; i <= lastRowNum; i++) { Row inputRow = inputSheet.getRow(i); if (inputRow == null) { // 处理空行,创建空行 outputSheet.createRow(startRowIndex); startRowIndex++; continue; } // 创建目标行 Row outputRow = outputSheet.createRow(startRowIndex); // 获取该行的最大列数 int lastCellNum = inputRow.getLastCellNum(); // 遍历每一个单元格 for (int j = 0; j < lastCellNum; j++) { Cell inputCell = inputRow.getCell(j); if (inputCell == null) { // 处理空单元格,创建空单元格(可选,根据需求调整) outputRow.createCell(j); continue; } // 创建目标单元格 Cell outputCell = outputRow.createCell(j); // 根据单元格类型复制值 copyCellValue(inputCell, outputCell); // 可选:复制单元格样式,让输出表和原表格式一致 outputCell.setCellStyle(inputCell.getCellStyle()); } startRowIndex++; } // 返回下一个可用的行索引 return startRowIndex; } // 复制单元格值的工具方法,处理不同类型的单元格 private static void copyCellValue(Cell inputCell, Cell outputCell) { CellType cellType = inputCell.getCellType(); switch (cellType) { case STRING: outputCell.setCellValue(inputCell.getStringCellValue()); break; case NUMERIC: if (DateUtil.isCellDateFormatted(inputCell)) { outputCell.setCellValue(inputCell.getDateCellValue()); } else { outputCell.setCellValue(inputCell.getNumericCellValue()); } break; case BOOLEAN: outputCell.setCellValue(inputCell.getBooleanCellValue()); break; case FORMULA: outputCell.setCellFormula(inputCell.getCellFormula()); break; case BLANK: outputCell.setCellType(CellType.BLANK); break; default: outputCell.setCellValue(inputCell.toString()); } } }
代码说明
- 把单表复制的逻辑抽取成了
copySingleSheet方法,避免重复代码,也更易维护。 - 复制第二个表时,传入第一个表复制完成后的行索引作为起始行,保证内容是追加而不是覆盖。
- 处理了空行、空单元格的情况,同时根据单元格类型正确复制值,避免格式错误。
- 可选复制单元格样式,让输出表和原表格式一致(如果不需要可以去掉该行代码)。
这样修改后,两个工作表的内容就会完整地复制到新工作表中,第一个表在上,第二个表在下,不会出现覆盖的问题。
内容的提问来源于stack exchange,提问作者Sonia Mathew
相关产品推荐
相关产品推荐

