【问题标题】:How to insert a row between two rows in an existing excel with HSSF (Apache POI)如何使用 HSSF(Apache POI)在现有 excel 的两行之间插入一行
【发布时间】:2011-08-12 17:53:20
【问题描述】:

不知何故,我设法在现有 Excel 文件的两行之间创建新行。问题是,一些格式没有包括在行的移动中。

其中之一是隐藏的行在班次期间相对不顺。我的意思是(例如),从 20 到 30 的行是隐藏的,但是当创建新行时,格式仍然存在。隐藏行在插入/创建新行期间也必须移动,它应该是 21 到 31。

另一件事是,工作表中不在单元格中的其他对象。就像文本框在新行创建后不会移动一样。它就像这些物体的位置是固定的。但我希望它移动,就像我在 excel 中插入新行或粘贴行一样。如果有插入新行的功能,请告诉我。

这就是我现在所拥有的,只是我的代码中的一个 sn-p。

HSSFWorkbook wb = new HSSFWorkbook(template); //template is the source of file
HSSFSheet sheet = wb.getSheet("SAMPLE");
HSSFRow newRow;
HSSFCell cellData;

int createNewRowAt = 9; //Add the new row between row 9 and 10

sheet.shiftRows(createNewRowAt, sheet.getLastRowNum(), 1, true, false);
newRow = sheet.createRow(createNewRowAt);
newRow = sheet.getRow(createNewRowAt);

如果可以复制和粘贴行,那将有很大帮助。但是我已经在这里问过了,找不到解决方案。所以我决定创建一个行作为临时解决方案。我已经完成了,但遇到了这样的问题。

任何帮助将不胜感激。谢谢!

【问题讨论】:

标签: java excel apache-poi poi-hssf


【解决方案1】:

here无耻地复制行的辅助函数

import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.util.CellRangeAddress;

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

public class RowCopy {

    public static void main(String[] args) throws Exception{
        HSSFWorkbook workbook = new HSSFWorkbook(new FileInputStream("c:/input.xls"));
        HSSFSheet sheet = workbook.getSheet("Sheet1");
        copyRow(workbook, sheet, 0, 1);
        FileOutputStream out = new FileOutputStream("c:/output.xls");
        workbook.write(out);
        out.close();
    }

    private static void copyRow(HSSFWorkbook workbook, HSSFSheet worksheet, int sourceRowNum, int destinationRowNum) {
        // Get the source / new row
        HSSFRow newRow = worksheet.getRow(destinationRowNum);
        HSSFRow sourceRow = worksheet.getRow(sourceRowNum);

        // If the row exist in destination, push down all rows by 1 else create a new row
        if (newRow != null) {
            worksheet.shiftRows(destinationRowNum, worksheet.getLastRowNum(), 1);
        } else {
            newRow = worksheet.createRow(destinationRowNum);
        }

        // Loop through source columns to add to new row
        for (int i = 0; i < sourceRow.getLastCellNum(); i++) {
            // Grab a copy of the old/new cell
            HSSFCell oldCell = sourceRow.getCell(i);
            HSSFCell newCell = newRow.createCell(i);

            // If the old cell is null jump to next cell
            if (oldCell == null) {
                newCell = null;
                continue;
            }

            // Copy style from old cell and apply to new cell
            HSSFCellStyle newCellStyle = workbook.createCellStyle();
            newCellStyle.cloneStyleFrom(oldCell.getCellStyle());
            ;
            newCell.setCellStyle(newCellStyle);

            // If there is a cell comment, copy
            if (oldCell.getCellComment() != null) {
                newCell.setCellComment(oldCell.getCellComment());
            }

            // If there is a cell hyperlink, copy
            if (oldCell.getHyperlink() != null) {
                newCell.setHyperlink(oldCell.getHyperlink());
            }

            // Set the cell data type
            newCell.setCellType(oldCell.getCellType());

            // Set the cell data value
            switch (oldCell.getCellType()) {
                case Cell.CELL_TYPE_BLANK:
                    newCell.setCellValue(oldCell.getStringCellValue());
                    break;
                case Cell.CELL_TYPE_BOOLEAN:
                    newCell.setCellValue(oldCell.getBooleanCellValue());
                    break;
                case Cell.CELL_TYPE_ERROR:
                    newCell.setCellErrorValue(oldCell.getErrorCellValue());
                    break;
                case Cell.CELL_TYPE_FORMULA:
                    newCell.setCellFormula(oldCell.getCellFormula());
                    break;
                case Cell.CELL_TYPE_NUMERIC:
                    newCell.setCellValue(oldCell.getNumericCellValue());
                    break;
                case Cell.CELL_TYPE_STRING:
                    newCell.setCellValue(oldCell.getRichStringCellValue());
                    break;
            }
        }

        // If there are are any merged regions in the source row, copy to new row
        for (int i = 0; i < worksheet.getNumMergedRegions(); i++) {
            CellRangeAddress cellRangeAddress = worksheet.getMergedRegion(i);
            if (cellRangeAddress.getFirstRow() == sourceRow.getRowNum()) {
                CellRangeAddress newCellRangeAddress = new CellRangeAddress(newRow.getRowNum(),
                        (newRow.getRowNum() +
                                (cellRangeAddress.getLastRow() - cellRangeAddress.getFirstRow()
                                        )),
                        cellRangeAddress.getFirstColumn(),
                        cellRangeAddress.getLastColumn());
                worksheet.addMergedRegion(newCellRangeAddress);
            }
        }
    }
}

【讨论】:

  • 我已经完成了对这段代码的测试,尽管它复制了行。但我目前的问题仍然存在。不管怎样,谢谢你的帮助,这段代码会有用处。
  • 您知道我们将如何支持在复制公式时对其进行调整吗?我正在复制说,第 1 行到第 2 行并希望包含公式。所以原始公式可能是第 1 行的 sum(A1:K1),现在我希望我的第 2 行(副本)有 sum(A2:K2)。目前,它会逐字复制公式,因此我可以使用 sum(A1:K1) 来获取两者。
  • 我已获取此代码,它对我有用。我做了两个小改动。 1) 使用 Row、Sheet 和 Workbook 的接口而不是 HSSF* 具体类 & 2) 从 src 行复制数据是可选的。
  • 这段代码会遇到运行时错误“java.lang.RuntimeException”尚未实现的任何特殊原因? :|相当困惑。
  • @Stupid.Fat.Cat 可以通过从 Workbook 类中删除 HSSF 前缀等来修复此错误。Workbook 是 HSSFWorkbook 的父类,因此,使用 Workbook 进行更广泛的使用更合乎逻辑.
【解决方案2】:

对于希望使用 XSSF (Apache POI) 在现有 excel 中的两行之间插入一行的人,XSSFSheet 中已经实现了一个方法“copyRows”。

import org.apache.poi.ss.usermodel.CellCopyPolicy;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileInputStream;
import java.io.FileOutputStream;

public class App2 throws Exception{
    public static void main(String[] args){
        XSSFWorkbook workbook = new XSSFWorkbook(new FileInputStream("input.xlsx"));
        XSSFSheet sheet = workbook.getSheet("Sheet1");
        sheet.copyRows(0, 2, 3, new CellCopyPolicy());
        FileOutputStream out = new FileOutputStream("output.xlsx");
        workbook.write(out);
        out.close();
    }
}

【讨论】:

  • 这不会插入新行,但将 shiftRows 和 copyRows 结合使用效果很好!
  • shiftRows 和 copyRows 作品查找 +1。但可悲的是,它非常缓慢。
【解决方案3】:

参考Qwerty's answer,您可以通过重复使用cellStyle来避免膨胀XL尺寸。 而当类型为CELL_TYPE_BLANK时,getStringCellValue返回""而不是null

private static void copyRow(Sheet worksheet, int sourceRowNum, int destinationRowNum) {
  // Get the source / new row
  Row newRow = worksheet.getRow(destinationRowNum);
  Row sourceRow = worksheet.getRow(sourceRowNum);

  // If the row exist in destination, push down all rows by 1 else create a new row
  if (newRow != null) {
    worksheet.shiftRows(destinationRowNum, worksheet.getLastRowNum(), 1);
  } else {
    newRow = worksheet.createRow(destinationRowNum);
  }

  // Loop through source columns to add to new row
  for (int i = 0; i < sourceRow.getLastCellNum(); i++) {
    // Grab a copy of the old/new cell
    Cell oldCell = sourceRow.getCell(i);
    Cell newCell = newRow.createCell(i);

    // If the old cell is null jump to next cell
    if (oldCell == null) {
      newCell = null;
      continue;
    }

    // Use old cell style
    newCell.setCellStyle(oldCell.getCellStyle());

    // If there is a cell comment, copy
    if (newCell.getCellComment() != null) {
      newCell.setCellComment(oldCell.getCellComment());
    }

    // If there is a cell hyperlink, copy
    if (oldCell.getHyperlink() != null) {
      newCell.setHyperlink(oldCell.getHyperlink());
    }

    // Set the cell data type
    newCell.setCellType(oldCell.getCellType());

    // Set the cell data value
    switch (oldCell.getCellType()) {
    case Cell.CELL_TYPE_BLANK:
      break;
    case Cell.CELL_TYPE_BOOLEAN:
      newCell.setCellValue(oldCell.getBooleanCellValue());
      break;
    case Cell.CELL_TYPE_ERROR:
      newCell.setCellErrorValue(oldCell.getErrorCellValue());
      break;
    case Cell.CELL_TYPE_FORMULA:
      newCell.setCellFormula(oldCell.getCellFormula());
      break;
    case Cell.CELL_TYPE_NUMERIC:
      newCell.setCellValue(oldCell.getNumericCellValue());
      break;
    case Cell.CELL_TYPE_STRING:
      newCell.setCellValue(oldCell.getRichStringCellValue());
      break;
    }
  }
}

【讨论】:

  • 备案,复制评论无效。首先,代码检查newCell.getCellComment(),当然是null。但是,修复if 也不起作用,因为注释将被移动而不是被复制。需要实例化一个新的Comment,这要复杂得多。见stackoverflow.com/questions/2281221/…
  • 如果您在 excel 文件的样式部分的大小方面遇到问题(即 .xlsx 文件中的 styles.xml 文件太大,堆使用量过多),请确保遵循此示例JVM,执行速度太慢等)
【解决方案4】:

引用Qwerty's answer,如果destRow不为null,sheet.shiftRows()会改变destRow对下一行的引用;所以我们应该总是创建一个新行:

if (destRow != null) {
  sheet.shiftRows(destination, sheet.getLastRowNum(), 1);
}
destRow = sheet.createRow(destination);

【讨论】:

  • 这是添加到 qwerty 答案的精确解决方案。谢谢老哥帮忙
【解决方案5】:

我在以下实现中合并了其他一些答案和 cmets,并使用 Apache POI v3.9 进行了测试。

我只有一个rownum 参数,因为我将目标行向下移动并将其复制到新的空行中。公式按预期处理,不会逐字复制,但有一个例外:对复制行上方的单元格的引用不会更新;解决方法是将这些显式引用(如果有)替换为使用INDIRECT() 计算的引用,如this post 建议的那样。

protected void copyRow(Sheet worksheet, int rowNum) {
    Row sourceRow = worksheet.getRow(rowNum);

    //Save the text of any formula before they are altered by row shifting
    String[] formulasArray = new String[sourceRow.getLastCellNum()];
    for (int i = 0; i < sourceRow.getLastCellNum(); i++) {
        if (sourceRow.getCell(i) != null && sourceRow.getCell(i).getCellType() == Cell.CELL_TYPE_FORMULA) 
            formulasArray[i] = sourceRow.getCell(i).getCellFormula();
    }

    worksheet.shiftRows(rowNum, worksheet.getLastRowNum(), 1);
    Row newRow = sourceRow;  //Now sourceRow is the empty line, so let's rename it
    sourceRow = worksheet.getRow(rowNum + 1);  //Now the source row is at rowNum+1

    // Loop through source columns to add to new row
    for (int i = 0; i < sourceRow.getLastCellNum(); i++) {
        // Grab a copy of the old/new cell
        Cell oldCell = sourceRow.getCell(i);
        Cell newCell;

        // If the old cell is null jump to next cell
        if (oldCell == null) {
            continue;
        } else {
            newCell = newRow.createCell(i);
        }

        // Copy style from old cell and apply to new cell
        CellStyle newCellStyle = worksheet.getWorkbook().createCellStyle();
        newCellStyle.cloneStyleFrom(oldCell.getCellStyle());
        newCell.setCellStyle(newCellStyle);

        // If there is a cell comment, copy
        if (oldCell.getCellComment() != null) {
            newCell.setCellComment(oldCell.getCellComment());
        }

        // If there is a cell hyperlink, copy
        if (oldCell.getHyperlink() != null) {
            newCell.setHyperlink(oldCell.getHyperlink());
        }

        // Set the cell data type
        newCell.setCellType(oldCell.getCellType());

        // Set the cell data value
        switch (oldCell.getCellType()) {
            case Cell.CELL_TYPE_BLANK:
                break;
            case Cell.CELL_TYPE_BOOLEAN:
                newCell.setCellValue(oldCell.getBooleanCellValue());
                break;
            case Cell.CELL_TYPE_ERROR:
                newCell.setCellErrorValue(oldCell.getErrorCellValue());
                break;
            case Cell.CELL_TYPE_FORMULA:
                newCell.setCellFormula(formulasArray[i]);
                break;
            case Cell.CELL_TYPE_NUMERIC:
                newCell.setCellValue(oldCell.getNumericCellValue());
                break;
            case Cell.CELL_TYPE_STRING:
                newCell.setCellValue(oldCell.getRichStringCellValue());
                break;
            default:   
                break; 
        }
    }

    // If there are any merged regions in the source row, copy to new row
    for (int i = 0; i < worksheet.getNumMergedRegions(); i++) {
        CellRangeAddress cellRangeAddress = worksheet.getMergedRegion(i);
        if (cellRangeAddress.getFirstRow() == sourceRow.getRowNum()) {
            CellRangeAddress newCellRangeAddress = new CellRangeAddress(newRow.getRowNum(),
                    (newRow.getRowNum() +
                            (cellRangeAddress.getLastRow() - cellRangeAddress.getFirstRow()
                                    )),
                    cellRangeAddress.getFirstColumn(),
                    cellRangeAddress.getLastColumn());
            worksheet.addMergedRegion(newCellRangeAddress);
        }
    }
}

我在生产代码中使用这个实现。

【讨论】:

    【解决方案6】:

    我在 Kotlin 中是这样实现的:

    fun Sheet.buildRow ( rowNum:Int ) : Row {
        val templateRow = this.getRow( rowNum )
        this.shiftRows( rowNum+1, sheet.lastRowNum, 1 )
        val newRow = this.createRow( rowNum+1 )
        templateRow.cellIterator().forEach {
            newRow.createCell( it.columnIndex ).cellStyle = it.cellStyle
        }
        return templateRow
    }
    

    它不会复制单元格值,只是复制格式。 应该也适用于 Java。

    【讨论】:

      【解决方案7】:

      关于在新行中“更新”的公式,由于所有复制都发生在移位之后,旧行(现在从新行向上一个索引)已经移动了它的公式,所以将它复制到新行row 将使新行引用旧行单元格。一个解决方案是在班次之前解析出公式,然后应用它们(一个简单的字符串数组就可以完成这项工作。我相信你可以在几行代码中编写代码)。

      函数开始时:

      ArrayList<String> fArray = new ArrayList<String>();
      Row origRow = sheet.getRow(sourceRow);
      for (int i = 0; i < origRow.getLastCellNum(); i++) {
          if (origRow.getCell(i) != null && origRow.getCell(i).getCellType() == Cell.CELL_TYPE_FORMULA) 
              fArray.add(origRow.getCell(i).getCellFormula());
          else fArray.add(null);
      }
      

      然后将公式应用于单元格时:

      newCell.setCellFormula(fArray.get(i));
      

      【讨论】:

        【解决方案8】:

        我最近遇到了同样的问题。我不得不在带有隐藏行的文档中插入新行,并且遇到了同样的问题。在 apache poi 列表中进行一些搜索和一些电子邮件之后,当文档具有隐藏行时,这似乎是 shiftrows() 中的错误。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-02-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多