如何在Apache POI中获取Excel空单元格的值?

45

我有一个巨大的 Excel 文件,有很多列,看起来像这样:

Column1 Column2 Column3 Column4 Column5
abc             def             ghi
        mno             pqr
......

这是我编写的打印这些值的代码:

try {
    FileInputStream inputStr = new FileInputStream(fileName);
    XSSFWorkbook xssfWork = new XSSFWorkbook(inputStr) ;
    XSSFSheet sheet1 = xssfWork.getSheetAt(0);
    Iterator rowItr = sheet1.rowIterator();

    while ( rowItr.hasNext() ) {
        XSSFRow row = (XSSFRow) rowItr.next();
        System.out.println("ROW:-->");
        Iterator cellItr = row.cellIterator();

        while ( cellItr.hasNext() ) {
            XSSFCell cell = (XSSFCell) cellItr.next();
            System.out.println("CELL:-->"+cell.toString());
        }
    }
} catch (Exception e) {
    e.printStackTrace();
}
这段代码生成的输出是:-
ROW:-->
CELL:-->Column1
CELL:-->Column2
CELL:-->Column3
CELL:-->Column4
CELL:-->Column5
ROW:-->
CELL:-->abc
CELL:-->def
CELL:-->ghi
ROW:-->
CELL:-->mno
CELL:-->pqr
所以,如果我们查看上面的输出,可以注意到POI库没有选择留空值的单元格,是否有一种方式可以将这些值作为null获取,或者一种识别跳过空白单元格呈现的值的方法?
谢谢。
9个回答

76
如果你想获取所有单元格,无论它们是否存在,那么迭代器就不适用于你。相反,你需要手动获取相应的单元格,很可能需要使用缺失单元格策略。
for(Row row : sheet) {
   for(int cn=0; cn<row.getLastCellNum(); cn++) {
       // If the cell is missing from the file, generate a blank one
       // (Works by specifying a MissingCellPolicy)
       Cell cell = row.getCell(cn, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
       // Print the cell for debugging
       System.out.println("CELL: " + cn + " --> " + cell.toString());
   }
}

Apache POI文档中有更详细的关于遍历单元格的说明


2
抱歉,row.getCell(short) 已经被弃用了。 - Buhake Sindi
1
确实如此!POI有大量的单元测试,证明它完全正常工作! - Gagravarr
在POI 3.9中,正确的语法似乎是row.getCell(cn, Row.CREATE_NULL_AS_BLANK)。 - pm_labs
6
更新:Row.CREATE_NULL_AS_BLANK 应该改为 MissingCellPolicy.CREATE_NULL_AS_BLANK - Jake Hm
你能得到的最简单的解决方案,这个解决方案对我非常有效。 - Deviprasad Sharma
显示剩余3条评论

14

我一直对这个问题感到沮丧。以下是我在poi-3.7-20101029和poi-3.8中发现的情况。

RowIterator和CellIterator不支持迭代NULL单元格或行,只支持物理定义的单元格(可以为空)。

返回我期望的解决方案需要使用基于0的Row.getCell([int], Row.CREATE_NULL_AS_BLANK),非常类似于Chavira的答案(假设有8个单元格行)。或者您可以使用Cell.columnIndex值,在迭代时检查跳过的数字...

令人恼火的是,在使用方法#1创建空单元格后,迭代器将返回已创建的BLANK单元格。我认为MissingCellPolicy被CellIterator忽略是一个bug。


1
遇到了同样的问题,使用基于0的索引方法(而不是foreach)和采用CREAT_NULL_AS_BLANK缺失行策略对我很有效。 - fimez

3
原因很简单:Excel文件可以包含尽可能多的行和列,因此返回所有可用的空白行和列将使单元格变得巨大且占用内存。
假设您有一个10x10的表格,在Excel中,它不是“确切地”10x10,因为您可以轻松添加带有空单元格的11x10,那么POI应该返回第11列吗?
实现您所请求的一种方法是使用HSSFCell.getColumnIndex()。
示例:
//Assuming your have a 2 dimensional array.
String[][] values = ......;// It is assigned

POIFSFileSystem fileSystem = new POIFSFileSystem(new FileInputStream(fileName));
HSSFWorkbook workbook = new HSSFWorkbook(fileSystem);

//Going through every worksheet.
for (int sheetPos = 0; sheetPos < workbook.getNumberOfSheets(); sheetPos++) {
    HSSFSheet sheet = workbook.getSheetAt(sheetPos);

    int rowPos = 0;
    Iterator<Row> rows = sheet.rowIterator();
    while (rows.hasNext()) {
        HSSFRow row = (HSSFRow) rows.next();

        Iterator<Cell> cells = row.cellIterator();
        while (cells.hasNext()) {
            HSSFCell cell = (HSSFCell) cells.next();
            String value = "";

            switch (cell.getCellType()) {
                case HSSFCell.CELL_TYPE_NUMERIC:
                    value = BigDecimal.valueOf(cell.getNumericCellValue()).toPlainString();
                    break;

                case HSSFCell.CELL_TYPE_STRING:
                    value = cell.getStringCellValue();
                    break;

                case HSSFCell.CELL_TYPE_BLANK:
                    value = "";
                    break;

                case HSSFCell.CELL_TYPE_FORMULA:
                    value = cell.getCellFormula();
                    break;

                default:
                    break;
            }

            values[rowPos][cell.getColumnIndex()] = value;
        }

        rowPos++;
    }
}

Row.getCell(int) 没有被废弃!我不知道你为什么会这样认为,但我可以向你保证它没有被废弃——请参见JavaDocs以获得证明。 - Gagravarr
@Gagravarr,抱歉,Row.getCell(short)已经被弃用了。 - Buhake Sindi

2
以下是我的解决方案。 "row.CREATE_NULL_AS_BLANK" 似乎无效,但这可能是因为我对 NPOI 不熟悉。
HSSFCell dataCell= (HSSFCell)row.GetCell(column, NPOI.SS.UserModel.MissingCellPolicy.CREATE_NULL_AS_BLANK);

3
如果你正在使用POI的.NET端口,那么可能有许多不同之处。而NPOI就是该端口的名称。请注意,我已经尽力保持翻译的准确性和简洁性,同时保留了原文的意思。 - Leigh

1
        for(org.apache.poi.ss.usermodel.Row tmp : hssfSheet){
            for(int i = 0; i<8;i++){
                System.out.println(tmp.getCell(i));
            }               
        }

他正在使用XSSF打开一个XLSX文件(2007+)。HSSF无法解决他的问题。 - Seth M.

0

这对我有用...

int rowNumber;
int previousCell;
int currentCell;
int currentRowNumber;
HSSFCell cell;

while (rows.hasNext()) {
    previousCell = -1;
    currentCell = 0;
    while (cellIterator.hasNext()) {
        cell = (HSSFCell) cellIterator.next();
        currentCell = cell.getColumnIndex();
        if (previousCell == currentCell-1)  {
            //...
        }
        else {
            System.out.println("Blank cell found");
        }
        previousCell = currentCell;
    }
}

0
public String[] rowToString(Row row)
{
    Iterator<Cell> cells = row.cellIterator() ;
    String[] data = new String[row.getLastCellNum()] ;

    int previousCell = 0 ;

    Cell cell = cells.next() ;
    int currentCell = cell.getColumnIndex();

    while (true)
    {
        if (previousCell == currentCell) {
            switch (cell.getCellType()) {
                case Cell.CELL_TYPE_NUMERIC:
                    data[previousCell] = cell.getNumericCellValue()+"" ;
                    break;
                case Cell.CELL_TYPE_STRING:
                    data[previousCell] = cell.getStringCellValue() ;
                    break;
                    /* // there could be other cases here.
                    case Cell.CELL_TYPE_FORMULA:
                        data[previousCell] =eval.evaluateFormulaCell(cell);
                        break;
                    case Cell.CELL_TYPE_BOOLEAN:
                        data[previousCell] = cell.getBooleanCellValue();
                        break;
                    case Cell.CELL_TYPE_BLANK:
                        data[previousCell] = "";
                        break;
                    case Cell.CELL_TYPE_ERROR:
                        data[previousCell] = "ERROR";
                        break;
                    */
            }
            if(cells.hasNext()){
                cell = cells.next() ;
                currentCell = cell.getColumnIndex();
            } else {
                break ;
            }

        } else {
            data[previousCell] = "";
        }
        previousCell++ ;

    }

    return data ;

}

请在您的答案中添加注释。 - dlavila
1
请在帖子中添加注释和解释,而不是下面。另外,请注意问题的年份。 - dlavila
该方法将Excel行转换为字符串数组,不跳过空白或空单元格值(在这种情况下,相应的数组值将只有一个空字符串)。 - Albe

0
List cellDataList = new ArrayList(); 

int lineNumber = 0;   

while (rowIterator.hasNext()) {
    HSSFRow hssfRow = (HSSFRow) rowIterator.next();
    //System.out.println("Befor If");
    lineNumber++;
    if(lineNumber==1){continue;}
    //System.out.println("Out side if ");

    Iterator<Cell> iterator = hssfRow.cellIterator();
    List<Cell> cellTempList = new ArrayList();
    int current = 0, next = 1;
    while (iterator.hasNext()) {
      Cell hssfCell = iterator.next();
      current = hssfCell.getColumnIndex();

      if(current<next){
          System.out.println("Condition Satisfied");
      }
      else{
          int loop = current-next;
          System.out.println("inside else Loop value : "+(loop));
          for(int k=0;k<loop+1;k++){
             System.out.println("Adding nulls");
             cellTempList.add(null);
             next = next + 1;
          }
      }

      cellTempList.add(hssfCell);

      next = next + 1;
      System.out.println("At End  next value is : "+next);
  }
  cellDataList.add(cellTempList);
}

0
for (Row row: sheet){
// This will return null if cell is empty / blank
Cell cell = row.getCell(columnNumber);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接