【问题解决】【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对象中注入使用;