如何使用 Apache POI 公式中的 excel 文本函数 "NUMBERVALUE"
How to use excel text function "NUMBERVALUE" from Apache POI formula
我正在使用 Apache POI 计算具有文本函数“NUMBERVALUE”的公式单元格,然后出现异常
原因:org.apache.poi.ss.formula.eval.NotImplementedFunctionException:_xlfn.NUMBERVALUE
有趣的是,当我尝试注册这个函数时 WorkbookEvaluator.registerFunction("NUMBERVALUE", new NumberRValue());
它给了我另一个错误:
java.lang.IllegalArgumentException: NUMBERVALUE 不是来自 Excel 分析工具包的函数。
我也尝试通过实现 FreeRefFunction 来使用用户定义的函数,它又回到了第一个错误。
原因:org.apache.poi.ss.formula.eval.NotImplementedFunctionException:_xlfn.NUMBERVALUE
如何在Apache POI中实现文字功能?
WorkbookEvaluator.registerFunction
仅适用于函数 apache poi
至少知道每个名称。这是通过以下方式列出的所有功能:
java.util.Collection<String> unsupportedFuncs = org.apache.poi.ss.formula.WorkbookEvaluator.getNotSupportedFunctionNames();
System.out.println(unsupportedFuncs);
所有列出的函数都可以使用 WorkbookEvaluator.registerFunction
注册,当在 org.apache.poi.hssf.record.formula.atp.AnalysisToolPak
时注册为 org.apache.poi.hssf.record.formula.functions.Function
,否则注册为 org.apache.poi.hssf.record.formula.functions.FreeRefFunction
。
但是 NUMBERVALUE
函数不在此列表中。所以这只能作为用户定义的函数添加。请参阅 apache poi
文档中的 User Defined Functions。
该函数必须实现 org.apache.poi.ss.formula.functions.FreeRefFunction
并且必须在 Workbook
.
的 UDF 工具包中注册
以下完整示例显示了 _xlfn.NUMBERVALUE
的基本实现。使用 NUMBERVALUE function 中的描述完成实施。到目前为止,它是一个工作草案,可能需要改进以更好地实现与 Excel 自己的结果的兼容性。
import java.io.FileInputStream;
import java.util.Locale;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.formula.functions.FreeRefFunction;
import org.apache.poi.ss.formula.udf.AggregatingUDFFinder;
import org.apache.poi.ss.formula.udf.DefaultUDFFinder;
import org.apache.poi.ss.formula.udf.UDFFinder;
import org.apache.poi.util.LocaleUtil;
public class EvaluateNUMBERVALUE {
public static void main( String[] args ) throws Exception {
Workbook workbook = WorkbookFactory.create(new FileInputStream("./ExcelWithNUMBERVALUE.xlsx"));
String[] functionNames = { "_xlfn.NUMBERVALUE" } ;
FreeRefFunction[] functionImpls = { new NumberValue() } ;
UDFFinder udfs = new DefaultUDFFinder( functionNames, functionImpls ) ;
UDFFinder udfToolpack = new AggregatingUDFFinder( udfs ) ;
workbook.addToolPack(udfToolpack);
LocaleUtil.setUserLocale(Locale.US);
FormulaEvaluator formulaEvaluator = workbook.getCreationHelper().createFormulaEvaluator();
DataFormatter dataFormatter = new DataFormatter();
for (Sheet sheet: workbook) {
for (Row row : sheet) {
for (Cell cell : row) {
String cellValue = dataFormatter.formatCellValue(cell, formulaEvaluator);
System.out.println(cellValue);
}
}
}
}
}
Class NumberValue
在上面的代码中使用:
import java.lang.NumberFormatException;
import java.util.Locale;
import java.text.DecimalFormatSymbols;
import org.apache.poi.ss.formula.OperationEvaluationContext;
import org.apache.poi.ss.formula.eval.ValueEval;
import org.apache.poi.ss.formula.eval.NumberEval;
import org.apache.poi.ss.formula.eval.ErrorEval;
import org.apache.poi.ss.formula.eval.EvaluationException;
import org.apache.poi.ss.formula.eval.OperandResolver;
import org.apache.poi.ss.formula.functions.FreeRefFunction;
import org.apache.poi.util.LocaleUtil;
public final class NumberValue implements FreeRefFunction {
@Override
public ValueEval evaluate( ValueEval[] args, OperationEvaluationContext ec ) {
Locale locale = LocaleUtil.getUserLocale();
DecimalFormatSymbols decimalFormatSymbols = new DecimalFormatSymbols(locale);
String text = null;
//If the Decimal_separator and Group_separator arguments are not specified, separators from the current locale are used.
String decSep = String.valueOf(decimalFormatSymbols.getDecimalSeparator());
String groupSep = String.valueOf(decimalFormatSymbols.getGroupingSeparator());
Double result = Double.NaN;
ValueEval v1 = null;
ValueEval v2 = null;
ValueEval v3 = null;
try {
if (args.length == 1) {
v1 = OperandResolver.getSingleValue( args[0], ec.getRowIndex(), ec.getColumnIndex());
text = OperandResolver.coerceValueToString(v1);
} else if (args.length == 2) {
v1 = OperandResolver.getSingleValue( args[0], ec.getRowIndex(), ec.getColumnIndex());
v2 = OperandResolver.getSingleValue( args[1], ec.getRowIndex(), ec.getColumnIndex());
text = OperandResolver.coerceValueToString(v1);
decSep = OperandResolver.coerceValueToString(v2).substring(0, 1); //If multiple characters are used in the Decimal_separator or Group_separator arguments, only the first character is used.
} else if (args.length == 3) {
v1 = OperandResolver.getSingleValue( args[0], ec.getRowIndex(), ec.getColumnIndex());
v2 = OperandResolver.getSingleValue( args[1], ec.getRowIndex(), ec.getColumnIndex());
v3 = OperandResolver.getSingleValue( args[2], ec.getRowIndex(), ec.getColumnIndex());
text = OperandResolver.coerceValueToString(v1);
decSep = OperandResolver.coerceValueToString(v2).substring(0, 1); //If multiple characters are used in the Decimal_separator or Group_separator arguments, only the first character is used.
groupSep = OperandResolver.coerceValueToString(v3).substring(0, 1); //If multiple characters are used in the Decimal_separator or Group_separator arguments, only the first character is used.
}
} catch (EvaluationException e) {
e.printStackTrace() ;
return e.getErrorEval();
}
if("".equals(text)) text = "0"; //If an empty string ("") is specified as the Text argument, the result is 0.
text = text.replace(" ", ""); //Empty spaces in the Text argument are ignored, even in the middle of the argument. For example, " 3 000 " is returned as 3000.
String[] parts = text.split("["+decSep+"]");
String sigPart = "";
String decPart = "";
if (parts.length > 2) return ErrorEval.VALUE_INVALID; //If a decimal separator is used more than once in the Text argument, NUMBERVALUE returns the #VALUE! error value.
if (parts.length > 1) {
sigPart = parts[0];
decPart = parts[1];
if (decPart.contains(groupSep)) return ErrorEval.VALUE_INVALID; //If the group separator occurs after the decimal separator in the Text argument, NUMBERVALUE returns the #VALUE! error value.
sigPart = sigPart.replace(groupSep, ""); //If the group separator occurs before the decimal separator in the Text argument , the group separator is ignored.
text = sigPart + "." + decPart;
} else if (parts.length > 0) {
sigPart = parts[0];
sigPart = sigPart.replace(groupSep, ""); //If the group separator occurs before the decimal separator in the Text argument , the group separator is ignored.
text = sigPart;
}
//If the Text argument ends in one or more percent signs (%), they are used in the calculation of the result.
//Multiple percent signs are additive if they are used in the Text argument just as they are if they are used in a formula.
//For example, =NUMBERVALUE("9%%") returns the same result (0.0009) as the formula =9%%.
int countPercent = 0;
while (text.endsWith("%")) {
countPercent++;
text = text.substring(0, text.length()-1);
}
try {
result = Double.valueOf(text);
result = result / Math.pow(100, countPercent); //If the Text argument ends in one or more percent signs (%), they are used in the calculation of the result.
checkValue(result);
} catch (EvaluationException e) {
e.printStackTrace() ;
return e.getErrorEval();
} catch (Exception anyex) {
return ErrorEval.VALUE_INVALID; //If any of the arguments are not valid, NUMBERVALUE returns the #VALUE! error value.
}
return new NumberEval(result);
}
static final void checkValue(double result) throws EvaluationException {
if (Double.isNaN(result) || Double.isInfinite(result)) {
throw new EvaluationException(ErrorEval.NUM_ERROR);
}
}
}
前缀_xlfn.
呢?
Excel 使用它来标记在 Excel 2007 之后引入的函数。前缀存储为函数名称的一部分。如果 Excel 版本知道该函数,因为它晚于 Excel 2007,则 GUI 将不会显示该前缀并评估该函数。如果 Excel 版本不知道该功能,则 GUI 将显示前缀以告知用户该不兼容性。参见 Issue: An _xlfn. prefix is displayed in front of a formula
由于存储了前缀,因此必须注册用户定义的函数,函数名称包括前缀:String[] functionNames = { "_xlfn.NUMBERVALUE" };
.
此代码 public 可用。它可以在任何类型的项目中免费重复使用。当然,我这边没有任何保证。
我正在使用 Apache POI 计算具有文本函数“NUMBERVALUE”的公式单元格,然后出现异常 原因:org.apache.poi.ss.formula.eval.NotImplementedFunctionException:_xlfn.NUMBERVALUE
有趣的是,当我尝试注册这个函数时 WorkbookEvaluator.registerFunction("NUMBERVALUE", new NumberRValue());
它给了我另一个错误:
java.lang.IllegalArgumentException: NUMBERVALUE 不是来自 Excel 分析工具包的函数。
我也尝试通过实现 FreeRefFunction 来使用用户定义的函数,它又回到了第一个错误。
原因:org.apache.poi.ss.formula.eval.NotImplementedFunctionException:_xlfn.NUMBERVALUE
如何在Apache POI中实现文字功能?
WorkbookEvaluator.registerFunction
仅适用于函数 apache poi
至少知道每个名称。这是通过以下方式列出的所有功能:
java.util.Collection<String> unsupportedFuncs = org.apache.poi.ss.formula.WorkbookEvaluator.getNotSupportedFunctionNames();
System.out.println(unsupportedFuncs);
所有列出的函数都可以使用 WorkbookEvaluator.registerFunction
注册,当在 org.apache.poi.hssf.record.formula.atp.AnalysisToolPak
时注册为 org.apache.poi.hssf.record.formula.functions.Function
,否则注册为 org.apache.poi.hssf.record.formula.functions.FreeRefFunction
。
但是 NUMBERVALUE
函数不在此列表中。所以这只能作为用户定义的函数添加。请参阅 apache poi
文档中的 User Defined Functions。
该函数必须实现 org.apache.poi.ss.formula.functions.FreeRefFunction
并且必须在 Workbook
.
以下完整示例显示了 _xlfn.NUMBERVALUE
的基本实现。使用 NUMBERVALUE function 中的描述完成实施。到目前为止,它是一个工作草案,可能需要改进以更好地实现与 Excel 自己的结果的兼容性。
import java.io.FileInputStream;
import java.util.Locale;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.formula.functions.FreeRefFunction;
import org.apache.poi.ss.formula.udf.AggregatingUDFFinder;
import org.apache.poi.ss.formula.udf.DefaultUDFFinder;
import org.apache.poi.ss.formula.udf.UDFFinder;
import org.apache.poi.util.LocaleUtil;
public class EvaluateNUMBERVALUE {
public static void main( String[] args ) throws Exception {
Workbook workbook = WorkbookFactory.create(new FileInputStream("./ExcelWithNUMBERVALUE.xlsx"));
String[] functionNames = { "_xlfn.NUMBERVALUE" } ;
FreeRefFunction[] functionImpls = { new NumberValue() } ;
UDFFinder udfs = new DefaultUDFFinder( functionNames, functionImpls ) ;
UDFFinder udfToolpack = new AggregatingUDFFinder( udfs ) ;
workbook.addToolPack(udfToolpack);
LocaleUtil.setUserLocale(Locale.US);
FormulaEvaluator formulaEvaluator = workbook.getCreationHelper().createFormulaEvaluator();
DataFormatter dataFormatter = new DataFormatter();
for (Sheet sheet: workbook) {
for (Row row : sheet) {
for (Cell cell : row) {
String cellValue = dataFormatter.formatCellValue(cell, formulaEvaluator);
System.out.println(cellValue);
}
}
}
}
}
Class NumberValue
在上面的代码中使用:
import java.lang.NumberFormatException;
import java.util.Locale;
import java.text.DecimalFormatSymbols;
import org.apache.poi.ss.formula.OperationEvaluationContext;
import org.apache.poi.ss.formula.eval.ValueEval;
import org.apache.poi.ss.formula.eval.NumberEval;
import org.apache.poi.ss.formula.eval.ErrorEval;
import org.apache.poi.ss.formula.eval.EvaluationException;
import org.apache.poi.ss.formula.eval.OperandResolver;
import org.apache.poi.ss.formula.functions.FreeRefFunction;
import org.apache.poi.util.LocaleUtil;
public final class NumberValue implements FreeRefFunction {
@Override
public ValueEval evaluate( ValueEval[] args, OperationEvaluationContext ec ) {
Locale locale = LocaleUtil.getUserLocale();
DecimalFormatSymbols decimalFormatSymbols = new DecimalFormatSymbols(locale);
String text = null;
//If the Decimal_separator and Group_separator arguments are not specified, separators from the current locale are used.
String decSep = String.valueOf(decimalFormatSymbols.getDecimalSeparator());
String groupSep = String.valueOf(decimalFormatSymbols.getGroupingSeparator());
Double result = Double.NaN;
ValueEval v1 = null;
ValueEval v2 = null;
ValueEval v3 = null;
try {
if (args.length == 1) {
v1 = OperandResolver.getSingleValue( args[0], ec.getRowIndex(), ec.getColumnIndex());
text = OperandResolver.coerceValueToString(v1);
} else if (args.length == 2) {
v1 = OperandResolver.getSingleValue( args[0], ec.getRowIndex(), ec.getColumnIndex());
v2 = OperandResolver.getSingleValue( args[1], ec.getRowIndex(), ec.getColumnIndex());
text = OperandResolver.coerceValueToString(v1);
decSep = OperandResolver.coerceValueToString(v2).substring(0, 1); //If multiple characters are used in the Decimal_separator or Group_separator arguments, only the first character is used.
} else if (args.length == 3) {
v1 = OperandResolver.getSingleValue( args[0], ec.getRowIndex(), ec.getColumnIndex());
v2 = OperandResolver.getSingleValue( args[1], ec.getRowIndex(), ec.getColumnIndex());
v3 = OperandResolver.getSingleValue( args[2], ec.getRowIndex(), ec.getColumnIndex());
text = OperandResolver.coerceValueToString(v1);
decSep = OperandResolver.coerceValueToString(v2).substring(0, 1); //If multiple characters are used in the Decimal_separator or Group_separator arguments, only the first character is used.
groupSep = OperandResolver.coerceValueToString(v3).substring(0, 1); //If multiple characters are used in the Decimal_separator or Group_separator arguments, only the first character is used.
}
} catch (EvaluationException e) {
e.printStackTrace() ;
return e.getErrorEval();
}
if("".equals(text)) text = "0"; //If an empty string ("") is specified as the Text argument, the result is 0.
text = text.replace(" ", ""); //Empty spaces in the Text argument are ignored, even in the middle of the argument. For example, " 3 000 " is returned as 3000.
String[] parts = text.split("["+decSep+"]");
String sigPart = "";
String decPart = "";
if (parts.length > 2) return ErrorEval.VALUE_INVALID; //If a decimal separator is used more than once in the Text argument, NUMBERVALUE returns the #VALUE! error value.
if (parts.length > 1) {
sigPart = parts[0];
decPart = parts[1];
if (decPart.contains(groupSep)) return ErrorEval.VALUE_INVALID; //If the group separator occurs after the decimal separator in the Text argument, NUMBERVALUE returns the #VALUE! error value.
sigPart = sigPart.replace(groupSep, ""); //If the group separator occurs before the decimal separator in the Text argument , the group separator is ignored.
text = sigPart + "." + decPart;
} else if (parts.length > 0) {
sigPart = parts[0];
sigPart = sigPart.replace(groupSep, ""); //If the group separator occurs before the decimal separator in the Text argument , the group separator is ignored.
text = sigPart;
}
//If the Text argument ends in one or more percent signs (%), they are used in the calculation of the result.
//Multiple percent signs are additive if they are used in the Text argument just as they are if they are used in a formula.
//For example, =NUMBERVALUE("9%%") returns the same result (0.0009) as the formula =9%%.
int countPercent = 0;
while (text.endsWith("%")) {
countPercent++;
text = text.substring(0, text.length()-1);
}
try {
result = Double.valueOf(text);
result = result / Math.pow(100, countPercent); //If the Text argument ends in one or more percent signs (%), they are used in the calculation of the result.
checkValue(result);
} catch (EvaluationException e) {
e.printStackTrace() ;
return e.getErrorEval();
} catch (Exception anyex) {
return ErrorEval.VALUE_INVALID; //If any of the arguments are not valid, NUMBERVALUE returns the #VALUE! error value.
}
return new NumberEval(result);
}
static final void checkValue(double result) throws EvaluationException {
if (Double.isNaN(result) || Double.isInfinite(result)) {
throw new EvaluationException(ErrorEval.NUM_ERROR);
}
}
}
前缀_xlfn.
呢?
Excel 使用它来标记在 Excel 2007 之后引入的函数。前缀存储为函数名称的一部分。如果 Excel 版本知道该函数,因为它晚于 Excel 2007,则 GUI 将不会显示该前缀并评估该函数。如果 Excel 版本不知道该功能,则 GUI 将显示前缀以告知用户该不兼容性。参见 Issue: An _xlfn. prefix is displayed in front of a formula
由于存储了前缀,因此必须注册用户定义的函数,函数名称包括前缀:String[] functionNames = { "_xlfn.NUMBERVALUE" };
.
此代码 public 可用。它可以在任何类型的项目中免费重复使用。当然,我这边没有任何保证。