2009-10-28 79 views
16

我正在使用Apache的POI来操纵使用Java的Excel(.xls)文件。POI/Excel:以“相对”的方式应用公式

我试图创建一个新的单元格,其内容是公式的结果,就好像用户复制/粘贴了公式(我称之为“相对”方式,与“绝对”相反)。

为了让自己更清楚,下面是一个简单的例子: 单元格A1包含“1”,B1包含“2”,A2包含“3”,B2包含“4”。 单元格A3包含以下公式“= A1 + B1”。 如果我将公式复制到excel下的A4单元格中,它将变成“= A2 + B2”:excel是动态调整公式的内容。

不幸的是,我不能以编程方式获得相同的结果。我发现的唯一解决方案是标记公式并自己做肮脏的工作,但我真的怀疑这应该是这样做的。我无法在指南或API中找到要找的内容。

有没有更简单的方法来解决这个问题?如果是这样的话,你能指点我正确的方向吗?

最好的问候,

尼尔斯

回答

2

我不觉得有什么。 POI将不得不解析公式(考虑到A1对$ A $ 1对$ A1等),我不相信它有这个能力。过去我一直这样做,我一直都必须自己管理。对不起 - 不是你希望的答案!

8

我也认为有没有简单的方法来做到这一点。

即使POI网站上的HSSF and XSSD examples例如TimesheetDemo手动进行配方施工。例如在线110

String ref = (char)('A' + j) + "3:" + (char)('A' + j) + "12"; 
cell.setCellFormula("SUM(" + ref + ")"); 
-2

你可以尝试一些第三方excel librarys,他们大多可以处理相对/绝对范围公式。

+0

既没有提出解决问题的具体库, – sven 2015-05-27 11:22:42

4

我向里面看了FormulaEvaluator类,发现一些POI内部类可以为我们做这项工作。

FormulaParser,其解析字符串的“解析东西”阵列:

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); 
    } 

,你准备好渲染“解析东西”回字符串:

formula = FormulaRenderer.toFormulaString(workbookWrapper, ptgs); 
cell.setCellFormula(formula); 
+0

这一个是超级有用,似乎是最简单的方法。两个补充: 1)应该指出,代码使用标记为POI内部的类(例如'XSSFEvaluationWorkbook'),所以此代码可能会在以后的版本中中断。 2)还应该调整描述区域的“AreaPtgBase”“事物”(例如'A1:C3')。该代码直接基于此答案中显示的'RefPtgBase'的处理。 – sven 2015-05-27 12:55:12

7

在我的感觉,user2622016是正确的,除了他的解决方案只能管理细胞引用,而不是区域引用(它不会为=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); 
} 

我仍然不知道如果我有它正确的所有单元格公式,但它为我工作,快速和可靠。

+0

2年后我抓住了吸管,但是你知道是否有与setColumn(),setRow(),setFirstColumn()和setLastColumn()等价的NPOI?或者如果它在POI/NPOI的最新版本中被停用? – justiceorjustus 2017-04-11 13:32:38

+1

@justiceorjustus:我知道我迟到了6年,但是在NPOI中,Row,Column,FirstRow,FirstColumn都有公共getter和setter。正因为如此,你可以写ref.Row + = shiftRows,... – tsukumogami 2017-07-20 05:05:17

3

的另一种方法以复制式相对,具有POI测试3.12

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; 
    } 

}