【问题标题】:Java POI : How to read Excel cell value and not the formula computing it?Java POI:如何读取 Excel 单元格值而不是计算公式?
【发布时间】:2011-09-30 09:34:36
【问题描述】:

我正在使用 Apache POI API 从 Excel 文件中获取值。 除了包含公式的单元格外,一切都很好。事实上,cell.getStringCellValue() 正在返回单元格中使用的公式,而不是单元格的值。

我尝试使用evaluateFormulaCell() 方法,但它不起作用,因为我使用的是 GETPIVOTDATA Excel 公式,并且此公式未在 API 中实现:

Exception in thread "main" org.apache.poi.ss.formula.eval.NotImplementedException: Error evaluating cell Landscape!K11
    at org.apache.poi.ss.formula.WorkbookEvaluator.addExceptionInfo(WorkbookEvaluator.java:321)
    at org.apache.poi.ss.formula.WorkbookEvaluator.evaluateAny(WorkbookEvaluator.java:288)
    at org.apache.poi.ss.formula.WorkbookEvaluator.evaluate(WorkbookEvaluator.java:221)
    at org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator.evaluateFormulaCellValue(HSSFFormulaEvaluator.java:320)
    at org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator.evaluateFormulaCell(HSSFFormulaEvaluator.java:213)
    at fromExcelToJava.ExcelSheetReader.unAutreTest(ExcelSheetReader.java:193)
    at fromExcelToJava.ExcelSheetReader.main(ExcelSheetReader.java:224)
Caused by: org.apache.poi.ss.formula.eval.NotImplementedException: GETPIVOTDATA
    at org.apache.poi.hssf.record.formula.functions.NotImplementedFunction.evaluate(NotImplementedFunction.java:42)

【问题讨论】:

    标签: java excel apache-poi notimplementedexception


    【解决方案1】:

    对于公式单元格,excel 存储两个东西。一个是公式本身,另一个是“缓存”值(论坛被评估为的最后一个值)

    如果您想获取最后一个缓存值(可能不再正确,但只要 Excel 保存了文件并且您没有更改它就应该如此),您需要类似:

     for(Cell cell : row) {
         if(cell.getCellType() == Cell.CELL_TYPE_FORMULA) {
            System.out.println("Formula is " + cell.getCellFormula());
            switch(cell.getCachedFormulaResultType()) {
                case Cell.CELL_TYPE_NUMERIC:
                    System.out.println("Last evaluated as: " + cell.getNumericCellValue());
                    break;
                case Cell.CELL_TYPE_STRING:
                    System.out.println("Last evaluated as \"" + cell.getRichStringCellValue() + "\"");
                    break;
            }
         }
     }
    

    【讨论】:

    • 缓存日期怎么样?日期单元格标记为 Cell.CELL_TYPE_NUMERIC。
    • 与普通日期单元格一样,您需要检查格式字符串以确定它是否为日期。没有变化,只是因为它是一个公式
    • 有什么理由让我们更喜欢 cell.getRichStringCellValue().toString()(如上面的代码所示)而不是 cell.getStringCellValue()?
    • 如何将TYPE_NUMERIC 格式化为我在excel中看到的值?例如,cell.getNumericCellValue() 得到了0.0,但我在 excel 中看到了0。我想得到0,而不是0.0
    • 根据本文档poi.apache.org/apidocs/dev/org/apache/poi/ss/usermodel/…,在 poi 版本 4.0.1 中,我不得不将代码从 case Cell.CELL_TYPE_NUMERIC: 稍微更改为 case NUMERIC: 等
    【解决方案2】:

    以前发布的解决方案对我不起作用。 cell.getRawValue() 返回与单元格中所述相同的公式。以下功能对我有用:

    public void readFormula() throws IOException {
        FileInputStream fis = new FileInputStream("Path of your file");
        Workbook wb = new XSSFWorkbook(fis);
        Sheet sheet = wb.getSheetAt(0);
        FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator();
    
        CellReference cellReference = new CellReference("C2"); // pass the cell which contains the formula
        Row row = sheet.getRow(cellReference.getRow());
        Cell cell = row.getCell(cellReference.getCol());
    
        CellValue cellValue = evaluator.evaluate(cell);
    
        switch (cellValue.getCellType()) {
            case Cell.CELL_TYPE_BOOLEAN:
                System.out.println(cellValue.getBooleanValue());
                break;
            case Cell.CELL_TYPE_NUMERIC:
                System.out.println(cellValue.getNumberValue());
                break;
            case Cell.CELL_TYPE_STRING:
                System.out.println(cellValue.getStringValue());
                break;
            case Cell.CELL_TYPE_BLANK:
                break;
            case Cell.CELL_TYPE_ERROR:
                break;
    
            // CELL_TYPE_FORMULA will never happen
            case Cell.CELL_TYPE_FORMULA:
                break;
        }
    
    }
    

    【讨论】:

    【解决方案3】:

    如果需要从 Excel 工作表中读取值并将它们作为字符串保存,例如将它们显示在某个地方或以文本文件格式使用它们,那么使用 DataFormatter 将是最好的。

    DataFormatter 能够从每个单元格值中获取一个字符串,无论单元格值本身是字符串、布尔值、数字、错误还是日期。这个字符串看起来与 Excel 将在其 GUI 的单元格中显示的一样。

    唯一的问题是公式单元格。直到 apache poi 5.1.0,在使用 DataFormatter 时需要一个 FormulaEvaluator 来评估公式。当apache poi 无法评估公式时,这将失败。从 5.2.0 开始,DataFormatter 可以设置为使用公式单元格的缓存值。如果 Excel 之前已经评估过公式,则不需要公式评估。

    完整示例:

    import org.apache.poi.ss.usermodel.*;
    import java.io.FileInputStream;
    
    class ReadExcel {
    
     public static void main(String[] args) throws Exception {
    
      Workbook workbook = WorkbookFactory.create(new FileInputStream("./ExcelExample.xlsx"));
      
      // up to apache poi 5.1.0 a FormulaEvaluator is needed to evaluate the formulas while using DataFormatter
      FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator();  
      
      DataFormatter dataFormatter = new DataFormatter(new java.util.Locale("en", "US"));
      // from 5.2.0 on the DataFormatter can set to use cached values for formula cells
      dataFormatter.setUseCachedValuesForFormulaCells(true);
      
      Sheet sheet = workbook.getSheetAt(0);
      for (Row row : sheet) {
       for (Cell cell : row) {
        //String value = dataFormatter.formatCellValue(cell, evaluator); // up to apache poi 5.1.0
        String value = dataFormatter.formatCellValue(cell); // from apache poi 5.2.0 on 
        System.out.println(value);
       }
      }
      workbook.close();
     }
    }
    

    【讨论】:

      【解决方案4】:

      还有一个替代命令,您可以在其中获取放置公式的单元格的原始值。它的返回类型是字符串。使用:

      cell.getRawValue();
      

      【讨论】:

      • 那个方法没有显示给我in the POI javadocs,你确定你的答案适用于这个库吗?
      • 啊,这只是 XSSF。你应该说清楚,因为 Common SS 模型没有它,HSSF 也没有
      • 这里的解决方法是从 Excel 工作表单元格中获取价值。它将支持excel支持的所有文件格式。
      • 不,不会,HSSF 不存在该方法,因此不适用于 .xls 文件 - 仅适用于 .xlsx
      【解决方案5】:

      如果您想从 HSSF 单元格中提取原始值,您可以使用类似以下代码片段:

      CellBase base = (CellBase) cell;
      CellType cellType = cell.getCellType();
      base.setCellType(CellType.STRING);
      String result = cell.getStringCellValue();
      base.setCellType(cellType);
      

      至少对于完全由数字组成的字符串(并由 Excel 自动转换为数字),这将返回原始字符串(例如 "12345")而不是小数值(例如 "12345.0")。请注意,setCellType 在接口 Cell(从 v. 4.1 开始)中可用,但在 v 5.x 中已弃用并宣布将其消除,而此方法在类 CellBase 中仍然可用。显然,在Cell 接口中使用getRawValue 或者至少能够在非STRING 单元格类型上使用getStringCellValue 会更好。不幸的是,描述中提到的setCellType 的所有替换都不会涵盖此用例(也许 POI 开发团队的成员会阅读此答案)。

      【讨论】:

        【解决方案6】:

        SelThroughJava 的回答非常有帮助,我必须对我的代码进行一些修改才能工作。 我使用 https://mvnrepository.com/artifact/org.apache.poi/poihttps://mvnrepository.com/artifact/org.testng/testng 作为依赖项。 下面给出了完整的代码以及精确的导入。

         import java.io.FileInputStream;
            import java.io.FileNotFoundException;
            import java.io.IOException;
        
            import org.apache.poi.hssf.usermodel.HSSFCell;
            import org.apache.poi.hssf.util.CellReference;
            import org.apache.poi.sl.usermodel.Sheet;
            import org.apache.poi.ss.usermodel.Cell;
            import org.apache.poi.ss.usermodel.CellType;
            import org.apache.poi.ss.usermodel.CellValue;
            import org.apache.poi.ss.usermodel.FormulaEvaluator;
            import org.apache.poi.ss.usermodel.Row;
            import org.apache.poi.ss.usermodel.Workbook;
            import org.apache.poi.ss.usermodel.WorkbookFactory;
            import org.apache.poi.xssf.usermodel.XSSFWorkbook;
        
        
            public class ReadExcelFormulaValue {
        
                private static final CellType NUMERIC = null;
                public static void main(String[] args) {
                    try {
                        readFormula();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                public static void readFormula() throws IOException {
                    FileInputStream fis = new FileInputStream("C:eclipse-workspace\\sam-webdbriver-diaries\\resources\\tUser_WS.xls");
                    org.apache.poi.ss.usermodel.Workbook workbook = WorkbookFactory.create(fis);
                    org.apache.poi.ss.usermodel.Sheet sheet = workbook.getSheetAt(0);
        
                    FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator();
        
                    CellReference cellReference = new CellReference("G2"); // pass the cell which contains the formula
                    Row row = sheet.getRow(cellReference.getRow());
                    Cell cell = row.getCell(cellReference.getCol());
        
                    CellValue cellValue = evaluator.evaluate(cell);
                    System.out.println("Cell type month  is "+cellValue.getCellTypeEnum());
                    System.out.println("getNumberValue month is  "+cellValue.getNumberValue());     
                  //  System.out.println("getStringValue "+cellValue.getStringValue());
        
        
                    cellReference = new CellReference("H2"); // pass the cell which contains the formula
                     row = sheet.getRow(cellReference.getRow());
                     cell = row.getCell(cellReference.getCol());
        
                    cellValue = evaluator.evaluate(cell);
                    System.out.println("getNumberValue DAY is  "+cellValue.getNumberValue());    
        
        
                }
        
            }
        

        【讨论】:

          猜你喜欢
          • 2015-04-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-06-03
          • 1970-01-01
          相关资源
          最近更新 更多