如何使用 shiftColumns 方法删除列

How to delete column with shiftColumns method

我想使用 Apache POI 5.0.0 删除 Excel (XLSX) 中的一列。我想使用 shiftColumns 方法,因此受影响的公式会自动调整。

void shiftColumns(int startColumn, int endColumn, int n)

https://poi.apache.org/apidocs/dev/org/apache/poi/ss/usermodel/Sheet.html

文档说,如果你想左移(覆盖左侧的列),你应该使用负数。

我在以下 Excel 文件上尝试了以下示例:

String path = "pathOfTheExcelFile";
File file = new File(path);
    
Workbook wb = null;

try (FileInputStream inputStream = new FileInputStream(file)) {

    wb = WorkbookFactory.create(inputStream); // read workbook

} catch (IOException e) {
    e.printStackTrace();
}
    
if(wb == null)
        return;
    
Sheet sheet = wb.getSheetAt(0); // read first sheet
    
// deleting / overriding 2nd column
sheet.shiftColumns(2, 5, -1); // shifting from 3rd to last column to the left
    
try (OutputStream fileOut = new FileOutputStream(path)) {
    wb.write(fileOut); // writing the result in the Excel file (ERROR)
}
catch (Exception e) {
    e.printStackTrace();
}
finally {
    try {
        if (wb != null)
            wb.close();
    }
    catch (IOException e) {
        e.printStackTrace();
    }
}

Excel 我要修改的文件:

执行代码后,我在第 wb.write(fileOut); 行收到此错误:

java.lang.IndexOutOfBoundsException
    at org.apache.xmlbeans.impl.store.Xobj.removeElement(Xobj.java:2099)
    at org.apache.xmlbeans.impl.store.Xobj.remove_element(Xobj.java:2130)
    at org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTRowImpl.removeC(CTRowImpl.java:173)
    at org.apache.poi.xssf.usermodel.XSSFRow.fixupCTCells(XSSFRow.java:612)
    at org.apache.poi.xssf.usermodel.XSSFRow.onDocumentWrite(XSSFRow.java:582)
    at org.apache.poi.xssf.usermodel.XSSFSheet.write(XSSFSheet.java:3625)
    at org.apache.poi.xssf.usermodel.XSSFSheet.commit(XSSFSheet.java:3570)
    at org.apache.poi.ooxml.POIXMLDocumentPart.onSave(POIXMLDocumentPart.java:465)
    at org.apache.poi.ooxml.POIXMLDocumentPart.onSave(POIXMLDocumentPart.java:470)
    at org.apache.poi.ooxml.POIXMLDocument.write(POIXMLDocument.java:221)
    at test.App.main(App.java:38)

仅供参考,我使用 Java 11 和这些库:

libraries

执行后,我的文件有0KB。

我在将工作簿保存在 Excel 文件中时调试了 Apache POI 库。这是问题开始的地方。也许对你有帮助:

pic1

pic2

pic3

是的,即使在最新版本的 Apache POI 5.1.0 中,Sheet.shiftColumns 仍然存在问题。 shiftColumns 在负向移动时没有正确删除单元格。这就是为什么写的时候出错了。

如果您明确删除要过度移动的列的单元格,则错误消失。因此,在将第三列(索引 2)向左移动之前,我们需要从第二列(索引 1)中删除所有单元格。

但是还有其他问题。即使是最新版本也不会在移动公式时更新计算链。这就是这个Q/A的问题:.

完整示例:

import java.io.FileInputStream;
import java.io.FileOutputStream;

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xssf.model.CalculationChain;
import org.apache.poi.ooxml.POIXMLDocumentPart;
import java.lang.reflect.Method;

public class ExcelShiftColums {

 private static void removeCalcChain(XSSFWorkbook workbook) throws Exception {
  CalculationChain calcchain = workbook.getCalculationChain();
  Method removeRelation = POIXMLDocumentPart.class.getDeclaredMethod("removeRelation", POIXMLDocumentPart.class); 
  removeRelation.setAccessible(true); 
  removeRelation.invoke(workbook, calcchain);
 }


 private static void removeColumn(Sheet sheet, int column) {
  for (Row row : sheet) {
   Cell cell = row.getCell(column);
   if (cell != null) {
    row.removeCell(cell);
   }
  }
 }
 
 private static int getLastFilledColumn(Sheet sheet) {
  int result = 0;
  for (Row row : sheet) {
   if (row.getLastCellNum() > result) result = row.getLastCellNum();
  }
  return result;     
 }

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

  String inFilePath = "./ExcelExampleIn.xlsx"; String outFilePath = "./ExcelExampleOut.xlsx";

  //String inFilePath = "./ExcelExampleIn.xls"; String outFilePath = "./ExcelExampleOut.xls";

  
  try (Workbook workbook = WorkbookFactory.create(new FileInputStream(inFilePath));
       FileOutputStream out = new FileOutputStream(outFilePath ) ) {

   Sheet sheet = workbook.getSheetAt(0);
   
   int lastFilledColumn = getLastFilledColumn(sheet);

   removeColumn(sheet, 1);
   sheet.shiftColumns(2, lastFilledColumn, -1);
   
   if (workbook instanceof XSSFWorkbook) removeCalcChain((XSSFWorkbook)workbook);
  
   workbook.write(out);
  }
 }
}