簡體   English   中英

如何使用 ZB713AE04A02A810D6F33DD956F42794Z POI 公式中的 excel 文本 function "NUMBERVALUE"

[英]How to use excel text function "NUMBERVALUE" from Apache POI formula

I am using Apache POI to evaluate a formula cell that has a text function "NUMBERVALUE", then I got an exception Caused by: org.apache.poi.ss.formula.eval.NotImplementedFunctionException: _xlfn.NUMBERVALUE

有趣的是,當我嘗試注冊這個 function WorkbookEvaluator.registerFunction("NUMBERVALUE", new NumberRValue()); 它給了我另一個錯誤: java.lang.IllegalArgumentException: NUMBERVALUE is not a function 來自 Excel 分析工具包。

我還嘗試通過實現 FreeRefFunction 來使用用戶定義的函數,它又回到了第一個錯誤。

引起:org.apache.poi.ss.formula.eval.NotImplementedFunctionException: _xlfn.NUMBERVALUE

如何在 Apache POI 中實現文本 function?

WorkbookEvaluator.registerFunction僅適用於函數apache poi至少知道每個名稱。 這是通過以下方式列出的所有功能:

java.util.Collection<String> unsupportedFuncs = org.apache.poi.ss.formula.WorkbookEvaluator.getNotSupportedFunctionNames();
System.out.println(unsupportedFuncs);

All the listed functions can be registered using WorkbookEvaluator.registerFunction either as org.apache.poi.hssf.record.formula.functions.Function , when in org.apache.poi.hssf.record.formula.atp.AnalysisToolPak or else as org.apache.poi.hssf.record.formula.functions.FreeRefFunction如果沒有。

NUMBERVALUE function 不在此列表中。 所以這只能作為用戶定義的 function 添加。 請參閱apache poi文檔的用戶定義函數

function 必須實現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 之后引入的功能。前綴存儲為 function 名稱的一部分。 If the Excel version knows that function because it is later than Excel 2007, then the GUI will not show that prefix and evaluates the function. 如果 Excel 版本不知道 function,則 GUI 將顯示前綴以通知用戶該不兼容。 請參閱問題:一個 _xlfn。 前綴顯示在公式前面

由於存儲了前綴,用戶定義 function 必須注冊為 function 名稱,包括前綴: String[] functionNames = { "_xlfn.NUMBERVALUE" }; .


此代碼是公開的。 它可以在任何類型的項目中免費重用。 當然,我這邊沒有任何保證。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM