【问题解决】【POI】记一次POI无法解析excel中函数的问题

【问题解决】【POI】记一次POI无法解析excel中函数的问题

目录:

        • 问题背景
        • 问题现象
        • 问题解决
        • 第一种
        • 第二种

问题背景

​ 在一次做POI解析Excel文件入库的过程中,发现一直报错,无法解析出数据;经过定位发现是解析excel的代码报异常导致,异常信息如Not found function exception 或者Not implement function exception,对源码进行跟踪,整理出两种在POI解析,但是excel中有POI中未实现的方法,分享整理以为笔记。

问题现象

​ 第一种:异常信息为Not implement function exception;

​ 第二种:异常信息为Not found function exception _xlfn.IFS;

问题解决

​ 第一种:定义自定义方法实现接口org.apache.poi.ss.formula.functions.Function

​ 第二种:定义自定义方法实现接口org.apache.poi.ss.formula.functions.FreeRefFunction

第一种

​ (1)找出POI未实现方法列表

* description: FunctionEval.getNotSupportedFunctionNames()获得的未实现函数列表实现接口Function,其他的实现接口FreeRefFunction
* NotSupportedFunctionNames:
* AREAS, ASC, AVERAGEA, BETADIST, BETAINV, BINOMDIST, CELL, CHIDIST, CHIINV, CHITEST, CONFIDENCE, CORREL, COVAR, CRITBINOM, DATEDIF, DATESTRING,
* DATEVALUE, DAVERAGE, DB, DBCS, DCOUNT, DCOUNTA, DDB, DMAX, DPRODUCT, DSTDEV, DSTDEVP, DSUM, DVAR, DVARP, EXPONDIST, FDIST, FINDB, FINV, FISHER,
* FISHERINV, FORECAST, FREQUENCY, FTEST, GAMMADIST, GAMMAINV, GAMMALN, GEOMEAN, GETPIVOTDATA, GROWTH, HARMEAN, HYPGEOMDIST, INFO, ISPMT, KURT,
* LEFTB, LENB, LINEST, LOGEST, LOGINV, LOGNORMDIST, MDETERM, MIDB, MINVERSE, MMULT, N, NEGBINOMDIST, NORMDIST, NORMINV, NORMSDIST, NORMSINV,
* NUMBERSTRING, PEARSON, PERCENTRANK, PERMUT, PHONETIC, PROB, QUARTILE, REPLACEB, RIGHTB, RSQ, SEARCHB, SKEW, SLN, STANDARDIZE,
* STDEVA, STDEVP, STDEVPA, STEYX, SYD, TDIST, TIMEVALUE, TINV, TRANSPOSE, TREND, TRIMMEAN, TTEST, TYPE, USDOLLAR, VARA, VARPA, VDB, WEIBULL, ZTEST

以上为POI未实现方法列表,以上都可以用第一种解决方式;

​ (2)例如方法AVERAGEA

​ excel有方法AVERAGEA,但是POI未实现,具体代码如下:

public class AverageaFunction implements Function {

    public static final String FUNCTION_NAME = "AVERAGEA";

    @Override
    public ValueEval evaluate(ValueEval[] valueEvals, int srcRowIndex, int srcColumnIndex) {
        if (valueEvals == null || valueEvals.length == 0) {
            return ErrorEval.VALUE_INVALID;
        }
        ValueEval valueEval = valueEvals[0];
        if (valueEval instanceof AreaEvalBase) {
            AreaEvalBase areaEval = (AreaEvalBase) valueEval;
            int firstRow = areaEval.getFirstRow();
            int lastRow = areaEval.getLastRow();
            int firstColumn = areaEval.getFirstColumn();
            int lastColumn = areaEval.getLastColumn();
            ValueEval average;
            if (areaEval.isColumn()) {
                average = getAvgeagea(getValueEvals(areaEval, firstColumn, firstRow, lastRow, false));
            } else if (areaEval.isRow()) {
                average = getAvgeagea(getValueEvals(areaEval, firstRow, firstColumn, lastColumn, true));
            } else {
                return ErrorEval.FUNCTION_NOT_IMPLEMENTED;
            }
            return average;
        }
        return ErrorEval.FUNCTION_NOT_IMPLEMENTED;
    }

    private List<ValueEval> getValueEvals(AreaEvalBase areaEval, int index, int start, int end, boolean  isRow) {
        List<ValueEval> valueEvals = new ArrayList<>();
        if (isRow) {
            for (int i = start; i <= end; i++) {
                valueEvals.add(areaEval.getAbsoluteValue(index, i));
            }
        } else {
            for (int i = start; i <= end; i++) {
                valueEvals.add(areaEval.getAbsoluteValue(i, index));
            }
        }
        return valueEvals;
    }

    private NumberEval getAvgeagea(List<ValueEval> valueEvals) {
        if (CollectionUtils.isEmpty(valueEvals)) {
            return NumberEval.ZERO;
        }
        int size = valueEvals.size();
        BigDecimal sum = BigDecimal.ZERO;
        for (ValueEval valueEval : valueEvals) {
            if (valueEval instanceof NumberEval) {
                NumberEval numberEval = (NumberEval) valueEval;
                sum = sum.add(BigDecimal.valueOf(numberEval.getNumberValue()));
            }
        }
        return new NumberEval(sum.divide(BigDecimal.valueOf(size), 2, BigDecimal.ROUND_HALF_UP).doubleValue());
    }
}

​ (3)使用方式,如下代码:

static {
        FunctionEval.registerFunction(AverageaFunction.FUNCTION_NAME, new AverageaFunction());
    }

全局静态注册,不能多次注册,否则会报错;

第二种

​ (1)例如方法IFS,excel中的多个条件函数,POI中未实现;代码如下:

public class IFSFunction implements FreeRefFunction {

    /**
     * 函数名
     */
    public static final String FUNCTION_NAME = "_xlfn.IFS";

    @Override
    public ValueEval evaluate(ValueEval[] valueEvals, OperationEvaluationContext operationEvaluationContext) {
        if (valueEvals == null || valueEvals.length % 2 != 0) {
            return ErrorEval.FUNCTION_NOT_IMPLEMENTED;
        }
        int length = valueEvals.length;
        for (int i = 0; i < length; i++) {
            if (i % 2 == 0) {
                ValueEval valueEval = valueEvals[i];
                if (valueEval instanceof BoolEval) {
                    BoolEval boolEval = (BoolEval) valueEval;
                    if (boolEval.getBooleanValue()) {
                        return valueEvals[i + 1];
                    }
                }
            }
        }
        return valueEvals[length - 1];
    }
}

​ (2)使用方法,如代码:

Workbook wb = WorkbookFactory.create(bis)) {
            wb.addToolPack(new AggregatingUDFFinder(new DefaultUDFFinder(new String[]{IFSFunction.FUNCTION_NAME}, new FreeRefFunction[]{new IFSFunction()})));

​ workbook对象中注入使用;

你可能感兴趣的:(解决问题,excel,java-ee)