使用Java 1.8与Apache POI 5.1.0读取Excel时第47行被跳过问题求助
问题描述
我使用的是Java 1.8、org.apache.poi 5.1.0以及org.apache.poi.ooxml 5.1.0版本。现有一个包含54行数据的Excel文件,我按5行一个块的规则读取该文件,当读取到第47行时该行被跳过,程序直接返回了新块的第一行,但预期应该返回新块上方的首个空行。
使用调试器可以看到程序直接从第46行跳转到了第48行,但我预期应当读取第47行。在第51行添加断点(位置见Java代码中的注释),可以看到currentRow的属性'r'从第46行直接跳到了第48行。
这个问题已经严重影响开发进度,导致程序无法正常使用。
以下是精简后的最小可复现相关代码:
build.gradle文件
plugins { id 'java' id 'application' } group 'nl.karnhuis' sourceCompatibility = 1.8 application { mainClass = 'nl.karnhuis.test.Testfile' } repositories { mavenCentral() maven { url "https://mvnrepository.com/artifact" } } dependencies { implementation 'org.apache.poi:poi:5.1.0' implementation 'org.apache.poi:poi-ooxml:5.1.0' }
gradle.settings文件
rootProject.name = 'testfile'
Java代码
package nl.karnhuis.test; import java.io.*; import java.util.*; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.*; public class Testfile { public void run() { File inputFile = new File("schema.xlsx"); handleFile(inputFile); } private void handleFile(File inputFile) { try { // 创建.xlsx文件对应的工作簿实例 XSSFWorkbook workbook = new XSSFWorkbook(inputFile); // 读取第一个sheet Sheet datatypeSheet = workbook.getSheetAt(0); Iterator<Row> iterator = datatypeSheet.iterator(); Row currentRow = null; // 遍历所有行 while (iterator.hasNext()) { if (checkForLastLine(currentRow)) { break; } currentRow = iterator.next(); // 前两行直接跳过 if ((currentRow.getRowNum()) < 2) { continue; } currentRow = iterator.next(); // 业务逻辑处理 currentRow = iterator.next(); // 业务逻辑处理 currentRow = iterator.next(); // 业务逻辑处理 // 下一行是空行,直接跳过 currentRow = iterator.next(); System.out.println(currentRow.getRowNum()); // 在此处添加断点 } } catch (IOException | InvalidFormatException e) { e.printStackTrace(); } } private boolean checkForLastLine(Row currentRow) { if (currentRow == null) { return false; } else { for (Cell currentCell : currentRow) { // 到达文件末尾则退出循环 return currentCell.getColumnIndex() == 0 && (currentCell.getStringCellValue().trim().startsWith("primaire") || currentCell.getStringCellValue().trim().startsWith("secondaire")); } } return false; } public static void main(String[] args) { Testfile mc = new Testfile(); mc.run(); } }
问题原因
POI 的 Sheet.iterator() 方法默认只返回Excel文件中实际存在的、有过编辑记录的行,完全未被编辑过的空白行不会被纳入迭代序列,你的第47行就是这种完全未初始化的空行,所以直接被迭代器跳过。
另外代码逻辑存在隐患:循环内连续调用5次iterator.next()却没有每次判断iterator.hasNext(),遇到文件末尾或者空行截断时,大概率会抛出NoSuchElementException。
修复方案
不要使用默认迭代器,改为按行号手动遍历,自己判断行的有效性,修改后的handleFile方法示例如下:
private void handleFile(File inputFile) { try { XSSFWorkbook workbook = new XSSFWorkbook(inputFile); Sheet datatypeSheet = workbook.getSheetAt(0); // 获取sheet实际的最后一行行号 int lastRowNum = datatypeSheet.getLastRowNum(); Row currentRow = null; // 从第0行开始按行号遍历 for (int rowNum = 0; rowNum <= lastRowNum; rowNum++) { if (checkForLastLine(currentRow)) { break; } // 不存在的行返回null,统一按空行处理 currentRow = datatypeSheet.getRow(rowNum); if (currentRow == null || currentRow.getRowNum() < 2) { continue; } // 按5行一个块规则读取,每次判断行号不越界 // 处理块内第一行 // 业务逻辑 rowNum++; if (rowNum > lastRowNum) break; currentRow = datatypeSheet.getRow(rowNum); // 处理块内第二行 // 业务逻辑 rowNum++; if (rowNum > lastRowNum) break; currentRow = datatypeSheet.getRow(rowNum); // 处理块内第三行 // 业务逻辑 rowNum++; if (rowNum > lastRowNum) break; currentRow = datatypeSheet.getRow(rowNum); // 处理块内第四行 // 业务逻辑 rowNum++; if (rowNum > lastRowNum) break; currentRow = datatypeSheet.getRow(rowNum); // 处理块内第五行(空行) System.out.println(currentRow != null ? currentRow.getRowNum() : rowNum); // 断点位置 } } catch (IOException | InvalidFormatException e) { e.printStackTrace(); } }
内容的提问来源于stack exchange,提问作者Rob The Ranger
相关产品推荐
相关产品推荐

