Java 如何使用ApachePOI在Excel中计算公式并避免;“保存更改”;留言箱?

Java 如何使用ApachePOI在Excel中计算公式并避免;“保存更改”;留言箱?,java,excel,apache-poi,xlsx,Java,Excel,Apache Poi,Xlsx,我遇到了以下问题:我编写了一个Java程序,将值写入xlsx文件。此xlsx文件使用公式计算新值。现在,我想从xlsx文件中获取这些计算值。问题是,我没有将计算出的值输入到Java程序中,因为没有保存更改。 因此,我尝试编辑xlsx文件中的xl/workbook.xml,以消除保存更改的问题。这是可行的,但现在我尝试读取的公式单元格返回默认值,而不是计算值。所以我有两个选择:我使用 workbook.setForceFormulaRecalculation(true) 计算不手动保存文件就无法

我遇到了以下问题:我编写了一个Java程序,将值写入xlsx文件。此xlsx文件使用公式计算新值。现在,我想从xlsx文件中获取这些计算值。问题是,我没有将计算出的值输入到Java程序中,因为没有保存更改。 因此,我尝试编辑xlsx文件中的xl/workbook.xml,以消除保存更改的问题。这是可行的,但现在我尝试读取的公式单元格返回默认值,而不是计算值。所以我有两个选择:我使用

workbook.setForceFormulaRecalculation(true)
计算不手动保存文件就无法读取的值。或者我编辑xl/workbook.xml以避免手动保存文件,但是公式不会计算值。。在这两种情况下,我的程序只能读取默认值,而不能读取我想要的计算值。。 以下是我编辑xml的代码:

 public void editXML(String path) throws FileNotFoundException, IOException{

    ZipFile zipFile = new ZipFile(path);
    final ZipOutputStream zos = new ZipOutputStream(new FileOutputStream("D:\\Excels\\SO_Berechnung_nosave.xlsx"));
    for(Enumeration e = zipFile.entries(); e.hasMoreElements();){
        ZipEntry entryIn = (ZipEntry) e.nextElement();
//    if(!(entryIn.getName().equalsIgnoreCase("xl/workbook.xml"))){
            System.out.println(entryIn.getName());
            zos.putNextEntry(entryIn);
            InputStream is = zipFile.getInputStream(entryIn);
            byte[] buffer = new byte[4096];
            int len;
            while((len = (is.read(buffer)))>0){
                zos.write(buffer, 0, len);
            }

//        } 
        zos.flush();
        zos.closeEntry();
    } 

    File excel = new File("D:\\Excels\\SO_Berechnung_nosave.xlsx");
    FileInputStream fis = new FileInputStream(excel);
    XSSFWorkbook book = new XSSFWorkbook(fis);
    FileOutputStream fos = new FileOutputStream("D:\\Excels\\SO_Berechnung_nosave.xlsx");
    book.setForceFormulaRecalculation(true);
    book.write(fos);
    fis.close();
    fos.flush();
    fos.close();

    for(Enumeration e = zipFile.entries(); e.hasMoreElements();){
        System.out.println("????????????????????????");
        ZipEntry entryIn = (ZipEntry) e.nextElement();
        if(entryIn.getName().equalsIgnoreCase("xl/workbook.xml")){
            System.out.println("RIGHT ENTRY FOUND AND WORKBOOK:XML WILL BE CHANGED NOW");
            zos.putNextEntry(new ZipEntry("xl\\workbook.xml"));
            System.out.println("RIGHT ENTRY FOUND AND WORKBOOK:XML WILL BE CHANGED NOW");
            InputStream is = zipFile.getInputStream(entryIn);
            byte[] buffer = new byte[2048];
            int len;
            while(is.read(buffer) >= 0){
                String s = new String(buffer);
                //Add standallone yes
                String sFirstLine = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
                String rFirstLine = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>";
                if(s.contains(sFirstLine)){
                    s = s.replace(sFirstLine, rFirstLine);
                    System.out.println("firstLine old: " + sFirstLine);
                    System.out.println("firstLine new: " + rFirstLine);
                }

                //replace wrong path
                String sPath = "\\Empty";
                String rPath = "";
                if(s.contains(sPath)){
                    s = s.replaceAll(Pattern.quote(sPath), Matcher.quoteReplacement(rPath));
                    System.out.println("path old: " + sPath);
                    System.out.println("path new: " + rPath);
                }

                //replace FileVersion
                String searchFileVersion = "/main\"><fileVersion appName=\"xl\" lastEdited=\"6\" lowestEdited=\"6\" rupBuild=\"14420\"/>"; //I know its 2times the same
                String replaceFileVersion =  "/main\"><fileVersion appName=\"xl\" lastEdited=\"6\" lowestEdited=\"6\" rupBuild=\"14420\"/>";//the rup Build should be right
                if(s.contains(searchFileVersion)){
                    s = s.replaceAll(searchFileVersion, replaceFileVersion);
                    System.out.println("fileVersion old: " + searchFileVersion);
                    System.out.println("fileVersion new: " + replaceFileVersion);
                }

                //replace calcId
                String searchCalcId = "<calcPr calcId=\"0\"/>";
                String replaceCalcId = "<calcPr calcId=\"152511\"/>"; //2147483647   152511
                if(s.contains(searchCalcId)){
                    s = s.replaceAll(searchCalcId, replaceCalcId);
                    System.out.println("calcId old: " + searchCalcId);
                    System.out.println("calcId new: " + replaceCalcId);
                }

                //replace Alternate
                String searchAlternateContent = "<mc:AlternateContent>";
                String replaceAlternateContent = "<mc:AlternateContent xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\">";
                if(s.contains(searchAlternateContent)){
                    s = s.replaceAll(searchAlternateContent, replaceAlternateContent);
                    System.out.println("AlternateContent old: " + searchAlternateContent);
                    System.out.println("AlternateContent new: " + replaceAlternateContent);
                }
                //idk if this has impact...
                String searchXmlns = "mc:Ignorable=\"x15\" "
                        + "xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" "
                        + "xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" "
                        + "xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" "
                        + "xmlns:x15=\"http://schemas.microsoft.com/office/spreadsheetml/2010/11/main\">";
                String replaceXmlns = "xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" "
                        + "xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" "
                        + "xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" "
                        + "mc:Ignorable=\"x15\" "
                        + "xmlns:x15=\"http://schemas.microsoft.com/office/spreadsheetml/2010/11/main\">";
                if(s.contains(searchXmlns)){
                    s = s.replaceAll(searchXmlns, replaceXmlns);
                    System.out.println("AlternateContent old: " + searchXmlns);
                    System.out.println("AlternateContent new: " + replaceXmlns);
                }

                //replace last line
                String sWb = "</workbook";
                String rWb = "</workbook>";
                if(s.contains(sWb)){
                    s = s.replaceAll(sWb, rWb);
                    System.out.println("Workbook old: " + sWb);
                    System.out.println("Workbook new: " + rWb);
                }

                System.out.println("");
                System.out.println(s);
                System.out.println("");

                len = s.trim().length();
                buffer = s.getBytes();
                zos.write(buffer, 0, (len < buffer.length) ? len : buffer.length);
            }
        }
    }

    zos.flush();
    zos.closeEntry();
    zos.close();


}
现在我得到了以下异常:

Exception in thread "AWT-EventQueue-0" java.lang.RuntimeException: Invalid ValueEval type passed for conversion: (class org.apache.poi.ss.formula.eval.MissingArgEval)
at org.apache.poi.ss.formula.functions.MultiOperandNumericFunction.collectValue(MultiOperandNumericFunction.java:219)
at org.apache.poi.ss.formula.functions.MultiOperandNumericFunction.collectValues(MultiOperandNumericFunction.java:179)
at org.apache.poi.ss.formula.functions.MultiOperandNumericFunction.getNumberArray(MultiOperandNumericFunction.java:128)
at org.apache.poi.ss.formula.functions.MultiOperandNumericFunction.evaluate(MultiOperandNumericFunction.java:90)
at org.apache.poi.ss.formula.OperationEvaluatorFactory.evaluate(OperationEvaluatorFactory.java:132)
at org.apache.poi.ss.formula.WorkbookEvaluator.evaluateFormula(WorkbookEvaluator.java:540)
at org.apache.poi.ss.formula.WorkbookEvaluator.evaluateAny(WorkbookEvaluator.java:303)
at org.apache.poi.ss.formula.WorkbookEvaluator.evaluate(WorkbookEvaluator.java:245)
at org.apache.poi.xssf.usermodel.XSSFFormulaEvaluator.evaluateFormulaCellValue(XSSFFormulaEvaluator.java:268)
at org.apache.poi.xssf.usermodel.XSSFFormulaEvaluator.evaluateFormulaCell(XSSFFormulaEvaluator.java:155)
at org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator.evaluateAllFormulaCells(HSSFFormulaEvaluator.java:335)
at org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator.evaluateAllFormulaCells(HSSFFormulaEvaluator.java:326)
at org.apache.poi.xssf.usermodel.XSSFFormulaEvaluator.evaluateAll(XSSFFormulaEvaluator.java:256)
at XLSXEditor.searchWriter(XLSXEditor.java:218)
at Main.fillTable(Main.java:962)
at Main.btShowActionPerformed(Main.java:715)
at Main.access$900(Main.java:25)
at Main$11.actionPerformed(Main.java:402)
at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2022)
at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2348)
at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:402)
at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:259)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:252)
at java.awt.Component.processMouseEvent(Component.java:6533)
at javax.swing.JComponent.processMouseEvent(JComponent.java:3324)
at java.awt.Component.processEvent(Component.java:6298)
at java.awt.Container.processEvent(Container.java:2236)
at java.awt.Component.dispatchEventImpl(Component.java:4889)
at java.awt.Container.dispatchEventImpl(Container.java:2294)
at java.awt.Component.dispatchEvent(Component.java:4711)
at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4888)
at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4525)
at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4466)
at java.awt.Container.dispatchEventImpl(Container.java:2280)
at java.awt.Window.dispatchEventImpl(Window.java:2746)
at java.awt.Component.dispatchEvent(Component.java:4711)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:758)
at java.awt.EventQueue.access$500(EventQueue.java:97)
at java.awt.EventQueue$3.run(EventQueue.java:709)
at java.awt.EventQueue$3.run(EventQueue.java:703)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:80)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:90)
at java.awt.EventQueue$4.run(EventQueue.java:731)
at java.awt.EventQueue$4.run(EventQueue.java:729)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:80)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:728)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)
我检查了公式中使用的所有函数,它们都提供了。。会不会因为本地化或其他原因而出现问题

“我编写了一个Java程序,将值写入xlsx文件 xlsx文件使用公式计算新值。现在我想得到这个 从xlsx文件中计算出的值。问题是,我没有 将计算出的值输入我的Java程序,因为 没有人得救。”

到目前为止,你的假设是正确的

“因此,我尝试编辑xlsx文件中的xl/workbook.xml以消除 这是一个保存更改的问题。”

但现在你走错了路

workbook.setforceFormulaRecomulation(true)
将公式的重新计算委托给
Excel
的GUI。如果下次
Excel
的GUI打开文件,则会进行重新计算。重新计算时,进行了更改。因此,关于保存更改的问题是合法的。只有在保存更改(即重新计算的公式结果)后,这些结果才会存储在文件中。然后,在从该文件新建
工作簿
之后,只能使用
apachepoi
读取结果

但是,授权重新计算只是一个
选项。另一个选项是使用and及其方法

...
workbook.getCreationHelper().createFormulaEvaluator().evaluateAll();
...
之后,所有公式都将使用ApachePOI进行评估,新结果将可重新读取

当然,这只适用于apache poi的
FormulaEvaluator
支持的公式。有些是不受支持的。然后,您不能简单地执行
evaluateAll
,而必须只计算包含支持公式的单元格

在这种情况下,关于这一问题的整章可能会引起人们的兴趣

更新:

如前所述,到目前为止,并非所有公式都得到支持。而且
apachepoi
不像
Excel
那样宽容

根据问题的更新部分中的错误,公式中使用了一个aka“遗漏参数”,通常不应包含此类遗漏参数

例如,
Excel
允许在表示
0
的公式中简单地不提供任何参数。例如,
=索引(A:A,匹配(“文本”,Z:Z,)
,而不是
=索引(A:A,匹配(“文本”,Z:Z,0))
。但是
apachepoi
FormulaEvaluator
不会容忍这种情况

因此,您现在需要调查导致错误的公式。因此,
evaluateAll
按照“重新计算工作簿中的所有公式”中的说明,在单元格上循环进行计算:

在那里,debug
System.out.println
应该告诉您是哪个单元格导致了问题

“我编写了一个Java程序,将值写入xlsx文件 xlsx文件使用公式计算新值。现在我想得到这个 从xlsx文件中计算出的值。问题是,我没有 将计算出的值输入我的Java程序,因为 没有人得救。”

到目前为止,你的假设是正确的

“因此,我尝试编辑xlsx文件中的xl/workbook.xml以消除 这是一个保存更改的问题。”

但现在你走错了路

workbook.setforceFormulaRecomulation(true)
将公式的重新计算委托给
Excel
的GUI。如果下次
Excel
的GUI打开文件,则会进行重新计算。重新计算时,进行了更改。因此,关于保存更改的问题是合法的。只有在保存更改(即重新计算的公式结果)后,这些结果才会存储在文件中。然后,在从该文件新建
工作簿
之后,只能使用
apachepoi
读取结果

但是,授权重新计算只是一个
选项。另一个选项是使用and及其方法

...
workbook.getCreationHelper().createFormulaEvaluator().evaluateAll();
...
之后,所有公式都将使用ApachePOI进行评估,新结果将可重新读取

当然,这只适用于apache poi的
FormulaEvaluator
支持的公式。有些是不受支持的。然后,您不能简单地执行
evaluateAll
,而必须只计算包含支持公式的单元格

在这种情况下,关于这一问题的整章可能会引起人们的兴趣

更新:

如前所述,到目前为止,并非所有公式都得到支持。而且
apachepoi
不像
Excel
那样宽容

根据问题的更新部分中的错误,公式中使用了一个aka“遗漏参数”,通常不应包含此类遗漏参数

例如
E
FormulaEvaluator evaluator = book.getCreationHelper().createFormulaEvaluator();
for (Sheet sheet : book) {
    for (Row r : sheet) {
        for (Cell c : r) {
            if (c.getCellType() == Cell.CELL_TYPE_FORMULA) {
System.out.println(c.getAddress() + ":" + c.getCellFormula());
                evaluator.evaluateFormulaCell(c);
            }
        }
    }
}