如何在 Apache POI 5.1.0 中使用数组溢出

How to use Array Spilling with Apache POI 5.1.0

使用 Apache POI 生成 excel 文件,是否可以防止 Excel 在公式中添加 implicit intersection operator (@)?

例如,使用下面的代码,我想要做的是使用 Excel Array Spilling behaviour 将列中的所有值从 A 复制到 K。但是,当使用 Excel Desktop(版本 16.54)打开文件时,它会自动在公式中添加 @ 运算符。

在工作簿 sheet sheet 的单元格 A1 中,我得到的是 =@IF(@otherSheet!A:K=""; ""; otherSheet!A:K) 而不是 =IF(otherSheet!A:K=""; ""; otherSheet!A:K),结果不一样,因为我只得到来自 anotherSheet.

的 A1 内的值
import org.apache.poi.ss.usermodel.CellType
import org.apache.poi.xssf.usermodel.XSSFWorkbook

import java.io.FileOutputStream
import java.nio.file._

object Main {
  def main(args: Array[String]): Unit = {

    val workbook = new XSSFWorkbook()
    val sheet = workbook.createSheet("sheet")
    val row = sheet.createRow(0)
    val cell = row.createCell(0, CellType.FORMULA)

    // Filling dummy data to another sheet
    val otherSheet = workbook.createSheet("otherSheet")
    val otherRow = otherSheet.createRow(0)
    for (i <- 0 to 10) {
      otherRow.createCell(i, CellType.STRING).setCellValue("Something")
    }

    // Copying values
    val otherSheetContent = f"otherSheet!A:K"
    cell.setCellFormula(f"""IF($otherSheetContent="", "", $otherSheetContent)""")
    println(cell.getCellFormula) // IF(otherSheet!A:K="", "", otherSheet!A:K)

    // Saving file
    val file = Paths.get("workbook.xlsx")
    workbook.write(new FileOutputStream(file.toFile))

  }
}

您不能使用 Excel 2007 年发布的 Dynamic array formulas and spilled array behaviour with Apache POI 5.1.0. The spilled array behaviour was introduced in Excel version 365. It is not usable in former versions. And Apache POI bases on Office Open XML。因此,使用 Apache POI 生成的 Excel 文件是 Excel 2007 年的文件。

为什么要添加@?这在 Implicit intersection operator: @:

的“我们什么时候将 @ 添加到旧公式?”部分中有说明

Generally speaking, functions that return multi-cell ranges or arrays will be prefixed with @ if they were authored in an older version of Excel. ... A common exception is if they are wrapped in a function that accepts an array or range (e.g. SUM() or AVERAGE()).

所以添加了 @,因为 IF 的任何参数都不期望数组。

使用 Apache POI 唯一可以实现的是设置遗留数组公式。参见示例:

import java.io.FileOutputStream;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFCell;

class CreateExcelArrayFormula {
    
 static void setArrayToFormula(XSSFCell cell, String ref) {
  if (cell.getCTCell().getF() != null) {
   cell.getCTCell().getF().setT(org.openxmlformats.schemas.spreadsheetml.x2006.main.STCellFormulaType.ARRAY);
   cell.getCTCell().getF().setRef(ref);
  }      
 }

 public static void main(String[] args) throws Exception {

  try (
       Workbook workbook = new XSSFWorkbook(); FileOutputStream fileout = new FileOutputStream("Excel.xlsx") ) {

   Sheet sheet = workbook.createSheet();
   Row row;
   Cell cell;
   
   // Filling dummy data to another sheet
   Sheet otherSheet = workbook.createSheet("otherSheet");
   for (int r = 0; r < 5; r++) {
    row = otherSheet.createRow(r);
    for (int c = 0; c < 11; c++) {
     row.createCell(c).setCellValue("OS-R" + (r+1) + "C" + (c+1));
    }
   }
   
   row = sheet.createRow(0);
   cell = row.createCell(0);
   cell.setCellFormula("IF(otherSheet!A1:K5=\"\", \"\", otherSheet!A1:K5)");
   if (cell instanceof XSSFCell) {
    setArrayToFormula((XSSFCell)cell, "A1:K5");
   }

   workbook.write(fileout);
  }

 }
}

但是您是否应该这样做,或者使用遗留数组公式的溢出数组行为?不,我认为你不应该。如果您使用公式 =IF(otherSheet!A:K="", "", otherSheet!A:K) 并使用溢出数组行为,则生成的文件将非常大。这是因为完整的列引用 A:K 跨越 1,048,576 行。与遗留阵列相同。 从不 应该使用具有完整列引用的数组。