问题在于Excel 365 和Excel 文件系统存储中通常以_xlfn 为前缀的新函数。
让我们有一个完整的例子来重现这个问题:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import org.apache.poi.ss.usermodel.*;
class ExcelSetCellFormula {
static final String WRONG = "IF(INDEX(CELL(\"width\",INDIRECT(ADDRESS(ROW(),COLUMN()))),1)<6,\"\",AGGREGATE(4,5,INDIRECT(ADDRESS(ROW()-1,COLUMN())):INDIRECT(ADDRESS(ROW()-1,COLUMN()))))";
static final String CORRECT = "IF(INDEX(CELL(\"width\",INDIRECT(ADDRESS(ROW(),COLUMN()))),1)<6,\"\",_xlfn.AGGREGATE(4,5,INDIRECT(ADDRESS(ROW()-1,COLUMN())):INDIRECT(ADDRESS(ROW()-1,COLUMN()))))";
public static void main(String[] args) throws Exception {
Workbook workbook = WorkbookFactory.create(new FileInputStream("./Excel.xlsx")); String filePath = "./ExcelNew.xlsx";
Sheet data = workbook.getSheetAt(0);
int r = 0;
int c = 4;
data.getRow(r+1).getCell(c,Row.MissingCellPolicy.CREATE_NULL_AS_BLANK).setCellFormula(WRONG);
//data.getRow(r+1).getCell(c,Row.MissingCellPolicy.CREATE_NULL_AS_BLANK).setCellFormula(CORRECT);
System.out.println(data.getRow(r+1).getCell(c));
FileOutputStream out = new FileOutputStream(filePath);
workbook.write(out);
out.close() ;
workbook.close();
}
}
Excel.xlsx 需要至少有一个工作表已经包含第 1 到第 2 行。代码在单元格E2中设置公式。
如果
static final String WRONG = "IF(INDEX(CELL(\"width\",INDIRECT(ADDRESS(ROW(),COLUMN()))),1)<6,\"\",AGGREGATE(4,5,INDIRECT(ADDRESS(ROW()-1,COLUMN())):INDIRECT(ADDRESS(ROW()-1,COLUMN()))))";
设置为公式,您正在使用Excel 365 打开生成的ExcelNew.xlsx,并且您尝试更改E2 中的公式,然后您会看到弹出窗口:
为什么@操作符在这里?
我们升级了 Excel 的公式语言
因此,您可能会注意到某些公式中的 @ 运算符。
您的公式的行为方式与以往相同。
这里指的是Implicit intersection operator: @。
但如果我们仔细观察,甚至没有理由在 IF 之前的公式中使用隐式交集运算符。那为什么要用呢?
原因是新的AGGREGATE 函数通常在Excels 文件系统存储中以_xlfn. 为前缀。但是Apache POI 没有这样做。所以Excel 365 在渲染时将该函数解释为用户定义的函数,并在其前面加上_xludf.。这就是#NAME? 错误的原因。这就是为什么要使用隐式交集运算符@。
如果
static final String CORRECT = "IF(INDEX(CELL(\"width\",INDIRECT(ADDRESS(ROW(),COLUMN()))),1)<6,\"\",_xlfn.AGGREGATE(4,5,INDIRECT(ADDRESS(ROW()-1,COLUMN())):INDIRECT(ADDRESS(ROW()-1,COLUMN()))))";
设置为公式,其中AGGREGATE以_xlfn.为前缀,#NAME?错误消失。并且Excel 365不使用隐式交集运算符@。