POI / Excel:以“相对”方式应用公式
我正在使用Apache的POI来操纵Java(.xls)文件。
我试图创build一个新的单元格,其内容是公式的结果,就好像用户复制/粘贴公式(我称之为“相对”方式,与“绝对”相反)。
为了让自己更清楚,下面是一个简单的例子:单元格A1包含“1”,B1包含“2”,A2包含“3”,B2包含“4”。 单元格A3包含以下公式“= A1 + B1”。 如果我将公式复制到excel下的A4单元格中,则变成“= A2 + B2”:excel是dynamic调整公式的内容。
不幸的是,我不能以编程方式得到相同的结果。 我find的唯一解决办法是标记公式,自己做肮脏的工作,但是我真的怀疑这应该是这样做的。 我无法在指南或API中find要找的内容。
有没有更简单的方法来解决这个问题? 如果是这样的话,你能指点一下正确的方向吗?
最好的祝福,
尼尔斯
我也觉得这样做并不容易。
即使是POI网站上的HSSF和XSSD示例 ,例如TimesheetDemo,也可以手动进行公式构build。 例如在110线附近
String ref = (char)('A' + j) + "3:" + (char)('A' + j) + "12"; cell.setCellFormula("SUM(" + ref + ")");
在我看来, user2622016是正确的,除了他的解决scheme只pipe理单元格引用,而不是区域引用(例如=SUM(A1:B8)
不起作用)。
以下是我如何解决这个问题:
private void copyFormula(Sheet sheet, Cell org, Cell dest) { if (org == null || dest == null || sheet == null || org.getCellType() != Cell.CELL_TYPE_FORMULA) return; if (org.isPartOfArrayFormulaGroup()) return; String formula = org.getCellFormula(); int shiftRows = dest.getRowIndex() - org.getRowIndex(); int shiftCols = dest.getColumnIndex() - org.getColumnIndex(); XSSFEvaluationWorkbook workbookWrapper = XSSFEvaluationWorkbook.create((XSSFWorkbook) sheet.getWorkbook()); Ptg[] ptgs = FormulaParser.parse(formula, workbookWrapper, FormulaType.CELL , sheet.getWorkbook().getSheetIndex(sheet)); for (Ptg ptg : ptgs) { if (ptg instanceof RefPtgBase) // base class for cell references { RefPtgBase ref = (RefPtgBase) ptg; if (ref.isColRelative()) ref.setColumn(ref.getColumn() + shiftCols); if (ref.isRowRelative()) ref.setRow(ref.getRow() + shiftRows); } else if (ptg instanceof AreaPtg) // base class for range references { AreaPtg ref = (AreaPtg) ptg; if (ref.isFirstColRelative()) ref.setFirstColumn(ref.getFirstColumn() + shiftCols); if (ref.isLastColRelative()) ref.setLastColumn(ref.getLastColumn() + shiftCols); if (ref.isFirstRowRelative()) ref.setFirstRow(ref.getFirstRow() + shiftRows); if (ref.isLastRowRelative()) ref.setLastRow(ref.getLastRow() + shiftRows); } } formula = FormulaRenderer.toFormulaString(workbookWrapper, ptgs); dest.setCellFormula(formula); }
我仍然不知道是否所有的细胞配方都是正确的,但对我来说,这是快速而可靠的。
我查看了FormulaEvaluator类的内部,发现了一些POI内部类可以为我们做的工作。
FormulaParser,它将Stringparsing为“parsing事物”的数组:
String formula = cell.getCellFormula(); XSSFEvaluationWorkbook workbookWrapper = XSSFEvaluationWorkbook.create((XSSFWorkbook) workbook); /* parse formula */ Ptg[] ptgs = FormulaParser.parse(formula, workbookWrapper, FormulaType.CELL, 0 /*sheet index*/ );
ptgs现在是我们的逆波兰表示法。 现在浏览所有元素,并根据需要逐一修改引用:
/* re-calculate cell references */ for( Ptg ptg : ptgs ) if( ptg instanceof RefPtgBase ) //base class for cell reference "things" { RefPtgBase ref = (RefPtgBase)ptg; if( ref.isColRelative() ) ref.setColumn( ref.getColumn() + 0 ); if( ref.isRowRelative() ) ref.setRow( ref.getRow() + 1 ); }
而且你已经准备好将“parsing事物”渲染回string了:
formula = FormulaRenderer.toFormulaString(workbookWrapper, ptgs); cell.setCellFormula( formula );
另一种相对复制公式的方法,用poi 3.12进行testing
public static void copyCellFormula(Workbook workbook, int sheetIndex, int rowIndex, int sourceColumnIndex, int destinationColumnIndex){ XSSFEvaluationWorkbook formulaParsingWorkbook = XSSFEvaluationWorkbook.create((XSSFWorkbook) workbook); SharedFormula sharedFormula = new SharedFormula(SpreadsheetVersion.EXCEL2007); Sheet sheet = workbook.getSheetAt(sheetIndex); Row lookupRow = sheet.getRow(rowIndex); Cell sourceCell = lookupRow.getCell(sourceColumnIndex); Ptg[] sharedFormulaPtg = FormulaParser.parse(sourceCell.getCellFormula(), formulaParsingWorkbook, FormulaType.CELL, sheetIndex); Ptg[] convertedFormulaPtg = sharedFormula.convertSharedFormulas(sharedFormulaPtg, 0, 1); Cell destinationCell = lookupRow.createCell(destinationColumnIndex); destinationCell.setCellFormula(FormulaRenderer.toFormulaString(formulaParsingWorkbook, convertedFormulaPtg)); }
根据需要更新共享公式:
sharedFormula.convertSharedFormulas(sharedFormulaPtg, rowIndexOffset, columnIndexOffset);
至于poi 3.12,SharedFormula不支持来自其他工作表的单元格参考/公式(='Sheet1'!A1)。 以下是SharedFormula的更新:
public class SharedFormula { private final int _columnWrappingMask; private final int _rowWrappingMask; public SharedFormula(SpreadsheetVersion ssVersion) { this._columnWrappingMask = ssVersion.getLastColumnIndex(); this._rowWrappingMask = ssVersion.getLastRowIndex(); } public Ptg[] convertSharedFormulas(Ptg[] ptgs, int formulaRow, int formulaColumn) { Ptg[] newPtgStack = new Ptg[ptgs.length]; RefPtgBase areaNPtg = null; AreaPtgBase var9 = null; Object ptg = null; byte originalOperandClass = 0; for(int k = 0; k < ptgs.length; ++k) { ptg = ptgs[k]; originalOperandClass = -1; if(!((Ptg)ptg).isBaseToken()) { originalOperandClass = ((Ptg)ptg).getPtgClass(); } if(ptg instanceof RefPtgBase) { if(ptg instanceof Ref3DPxg) { areaNPtg = (Ref3DPxg)ptg; this.fixupRefRelativeRowAndColumn(areaNPtg, formulaRow, formulaColumn); ptg = areaNPtg; }else if(ptg instanceof Ref3DPtg) { areaNPtg = (Ref3DPtg)ptg; this.fixupRefRelativeRowAndColumn(areaNPtg, formulaRow, formulaColumn); ptg = areaNPtg; }else { areaNPtg = (RefPtgBase)ptg; ptg = new RefPtg(this.fixupRelativeRow(formulaRow, areaNPtg.getRow(), areaNPtg.isRowRelative()), this.fixupRelativeColumn(formulaColumn, areaNPtg.getColumn(), areaNPtg.isColRelative()), areaNPtg.isRowRelative(), areaNPtg.isColRelative()); } ((Ptg)ptg).setClass(originalOperandClass); }else if(ptg instanceof AreaPtgBase) { if(ptg instanceof Area3DPxg) { var9 = (Area3DPxg)ptg; this.fixupAreaRelativeRowAndColumn(var9, formulaRow, formulaColumn); ptg = var9; }else if(ptg instanceof Area3DPxg) { var9 = (Area3DPtg)ptg; this.fixupAreaRelativeRowAndColumn(var9, formulaRow, formulaColumn); ptg = var9; }else { var9 = (AreaPtgBase)ptg; ptg = new AreaPtg(this.fixupRelativeRow(formulaRow, var9.getFirstRow(), var9.isFirstRowRelative()), this.fixupRelativeRow(formulaRow, var9.getLastRow(), var9.isLastRowRelative()), this.fixupRelativeColumn(formulaColumn, var9.getFirstColumn(), var9.isFirstColRelative()), this.fixupRelativeColumn(formulaColumn, var9.getLastColumn(), var9.isLastColRelative()), var9.isFirstRowRelative(), var9.isLastRowRelative(), var9.isFirstColRelative(), var9.isLastColRelative()); } ((Ptg)ptg).setClass(originalOperandClass); }else if(ptg instanceof OperandPtg) { ptg = ((OperandPtg)ptg).copy(); } newPtgStack[k] = (Ptg)ptg; } return newPtgStack; } protected void fixupRefRelativeRowAndColumn(RefPtgBase areaNPtg, int formulaRow, int formulaColumn){ areaNPtg.setRow(this.fixupRelativeRow(formulaRow, areaNPtg.getRow(), areaNPtg.isRowRelative())); areaNPtg.setColumn(this.fixupRelativeColumn(formulaColumn, areaNPtg.getColumn(), areaNPtg.isColRelative())); areaNPtg.setRowRelative(areaNPtg.isRowRelative()); areaNPtg.setColRelative(areaNPtg.isColRelative()); } protected void fixupAreaRelativeRowAndColumn(AreaPtgBase var9, int formulaRow, int formulaColumn){ var9.setFirstRow(this.fixupRelativeRow(formulaRow, var9.getFirstRow(), var9.isFirstRowRelative())); var9.setLastRow(this.fixupRelativeRow(formulaRow, var9.getLastRow(), var9.isLastRowRelative())); var9.setFirstColumn(this.fixupRelativeColumn(formulaColumn, var9.getFirstColumn(), var9.isFirstColRelative())); var9.setLastColumn(this.fixupRelativeColumn(formulaColumn, var9.getLastColumn(), var9.isLastColRelative())); var9.setFirstRowRelative(var9.isFirstRowRelative()); var9.setLastRowRelative(var9.isLastRowRelative()); var9.setFirstColRelative(var9.isFirstColRelative()); var9.setLastColRelative(var9.isLastColRelative()); } protected int fixupRelativeColumn(int currentcolumn, int column, boolean relative) { return relative?column + currentcolumn & this._columnWrappingMask:column; } protected int fixupRelativeRow(int currentrow, int row, boolean relative) { return relative?row + currentrow & this._rowWrappingMask:row; } }
我不认为有。 POI将不得不parsing公式(考虑A1对$ A $ 1对$ A1等),我不相信它有这个容量。 过去我做这件事的时候,我总是不得不亲自处理这件事。 对不起 – 不是你所希望的答案!
你可以尝试一些第三方Excel图书馆,他们大多可以处理相对/绝对范围公式。