【探讨】Java POI 处理 Excel 中的名称管理器

前言

最近遇到了一些导表的问题。原本的导表工具导不了使用名称管理器的Excel。
首先我们有两个Sheet。B1用的是名称管理器中的AAA, 而B2用的对应的公式。
【探讨】Java POI 处理 Excel 中的名称管理器_第1张图片
【探讨】Java POI 处理 Excel 中的名称管理器_第2张图片
第二个sheet,名为Test2:
【探讨】Java POI 处理 Excel 中的名称管理器_第3张图片

这是一段简化的代码:

public class Main {
    public static void main(String[] args) {
        var inputFile = new File("src/main/java/poi/test.xlsx");
        var dataFormatter = new DataFormatter();
        try (var wb = new XSSFWorkbook(new FileInputStream(inputFile))) {
            wb.setForceFormulaRecalculation(true);
            var sheet = wb.getSheet("Test");
            var formulaEvaluator = wb.getCreationHelper().createFormulaEvaluator();
            for (int i = sheet.getFirstRowNum(); i <= sheet.getLastRowNum(); i++) {
                var row = sheet.getRow(i);
                var list = new ArrayList<String>();
                for (var cell : row) {
                    list.add(format(dataFormatter, formulaEvaluator, cell));
                }
                System.out.println(String.join(", ", list));
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    public static String format(DataFormatter dataFormatter, XSSFFormulaEvaluator formulaEvaluator, Cell cell) {
        return dataFormatter.formatCellValue(cell, formulaEvaluator);
    }
}

控制台的输出为:

1, #N/A
2, b

这意味着无法解析这个命名统计后的值。

为啥公式可以执行。但是从名称管理器拿不到?是否可以通过拿名称对应的公式来计算出相应的结果?

通过修改formatter:

    public static String format(DataFormatter dataFormatter, XSSFFormulaEvaluator formulaEvaluator, Cell cell) {
        if (cell.getCellType() == CellType.FORMULA) {
            System.out.println("Formula: " + cell.getCellFormula());
            System.out.println("RichString: " + cell.getRichStringCellValue().getString());
            System.out.println("Cache result type: " + cell.getCachedFormulaResultType());
        }
        return dataFormatter.formatCellValue(cell, formulaEvaluator);
    }

我们可以得到输出:

Formula: AAA
RichString: a
Cache result type: STRING
1, #N/A
---------------------------------------
Formula: VLOOKUP(A2,Test2!$A$1:$B$5,2)
RichString: b
Cache result type: STRING
2, b
---------------------------------------

这么看来RichString可以拿到我们想要的值。
但是当我把Sheet2中对应的值改为数字:
【探讨】Java POI 处理 Excel 中的名称管理器_第4张图片

得到的一个保存的结果:

Formula: AAA
Exception in thread "main" java.lang.IllegalStateException: Cannot get a STRING value from a NUMERIC formula cell
	at org.apache.poi.xssf.usermodel.XSSFCell.typeMismatch(XSSFCell.java:946)
	at org.apache.poi.xssf.usermodel.XSSFCell.getRichStringCellValue(XSSFCell.java:330)
	at org.apache.poi.xssf.usermodel.XSSFCell.getRichStringCellValue(XSSFCell.java:77)
	at poi.Main.format(Main.java:43)
	at poi.Main.main(Main.java:29)

这里我想拿到一个可以区分是Name还是Formula的属性。但是我看了下从cell的方法好像没办法拿到。

一个解决方案

package poi;

import org.apache.poi.ss.SpreadsheetVersion;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.ss.util.AreaReference;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.xssf.usermodel.XSSFFormulaEvaluator;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        var inputFile = new File("src/main/java/poi/test.xlsx");
        var dataFormatter = new DataFormatter();
        try (var wb = new XSSFWorkbook(new FileInputStream(inputFile))) {
            wb.setForceFormulaRecalculation(true);
            var sheet = wb.getSheet("Test");
            var formulaEvaluator = wb.getCreationHelper().createFormulaEvaluator();
            for (int i = sheet.getFirstRowNum(); i <= sheet.getLastRowNum(); i++) {
                var row = sheet.getRow(i);
                var list = new ArrayList<String>();
                for (var cell : row) {
                    list.add(format(dataFormatter, formulaEvaluator, cell));
                }
                System.out.println(String.join(", ", list));
                System.out.println("---------------------------------------");
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    public static String format(DataFormatter dataFormatter, XSSFFormulaEvaluator formulaEvaluator, Cell cell) {
        if (cell.getCellType() == CellType.FORMULA) {
            System.out.println("Formula: " + cell.getCellFormula());
            System.out.println("Cache result type: " + cell.getCachedFormulaResultType());
            if (cell.getCachedFormulaResultType() == CellType.STRING) {
                return cell.getRichStringCellValue().getString();
            } else {
                return String.valueOf(cell.getNumericCellValue());
            }
        }
        return dataFormatter.formatCellValue(cell, formulaEvaluator);
    }
}

执行后得到结果:

Formula: AAA
Cache result type: NUMERIC
1, 1.0
---------------------------------------
Formula: VLOOKUP(A2,Test2!$A$1:$B$5,2)
Cache result type: NUMERIC
2, 2.0
---------------------------------------

这样结果勉强可以用,但是感觉上这段代码是有BUG的,但是对POI的了解不是太透,本文相当于抛砖引玉,如果有懂的可以探讨下。

你可能感兴趣的:(java,excel)