30

I'm using the Apache POi HSSF library to import info into my application. The problem is that the files have some extra/empty rows that need to be removed first before parsing.

There's not a HSSFSheet.removeRow( int rowNum ) method. Only removeRow( HSSFRow row ). The problem with this it that empty rows can't be removed. For example:

sheet.removeRow( sheet.getRow(rowNum) );

gives a NullPointerException on empty rows because getRow() returns null. Also, as I read on forums, removeRow() only erases the cell contents but the row is still there as an empty row.

Is there a way of removing rows (empty or not) without creating a whole new sheet without the rows that I want to remove?

Juha Syrjälä
  • 33,425
  • 31
  • 131
  • 183
fmaste
  • 383
  • 1
  • 5
  • 7

7 Answers7

34
 /**
 * Remove a row by its index
 * @param sheet a Excel sheet
 * @param rowIndex a 0 based index of removing row
 */
public static void removeRow(HSSFSheet sheet, int rowIndex) {
    int lastRowNum=sheet.getLastRowNum();
    if(rowIndex>=0&&rowIndex<lastRowNum){
        sheet.shiftRows(rowIndex+1,lastRowNum, -1);
    }
    if(rowIndex==lastRowNum){
        HSSFRow removingRow=sheet.getRow(rowIndex);
        if(removingRow!=null){
            sheet.removeRow(removingRow);
        }
    }
}
AndreAY
  • 341
  • 3
  • 3
  • 1
    this solution is not working in the case of multiple row deletion. – gospodin Nov 30 '12 at 18:08
  • @gospodin - Run the inside code in a loop and don't forget to decrease the rowIndex counter by 1 everytime you delete a row. – Avik Jun 20 '14 at 03:15
  • 2
    There is currently a bug in POI that will throw >org.apache.xmlbeans.impl.values.XmlValueDisconnectedException< when using this function more than once on ranges that contain common cells - https://bz.apache.org/bugzilla/show_bug.cgi?id=59733 – beermann Dec 02 '16 at 14:41
  • PLEASE add some spaces to your if statements, it's so hard to read without any. Thanks for that answer anyways. – SimonH Sep 24 '17 at 10:09
  • On some I get: java.lang.RuntimeException: CountryRecord not found. Reported here https://bz.apache.org/bugzilla/show_bug.cgi?id=55505 with the comment resaving in Excel fixed. However that record is not deleted. Is there a reliable non-file based solution. – cp. Jun 04 '20 at 01:30
9

I know, this is a 3 year old question, but I had to solve the same problem recently, and I had to do it in C#. And here is the function I'm using with NPOI, .Net 4.0

    public static void DeleteRow(this ISheet sheet, IRow row)
    {
        sheet.RemoveRow(row);   // this only deletes all the cell values

        int rowIndex = row.RowNum;

        int lastRowNum = sheet.LastRowNum;

        if (rowIndex >= 0 && rowIndex < lastRowNum)
        {
            sheet.ShiftRows(rowIndex + 1, lastRowNum, -1);
        }
    }
trailmax
  • 34,305
  • 22
  • 140
  • 234
4

Something along the lines of

int newrownum=0;
for (int i=0; i<=sheet.getLastRowNum(); i++) {
  HSSFRow row=sheet.getRow(i);
  if (row) row.setRowNum(newrownum++);
}

should do the trick.

Am_I_Helpful
  • 18,735
  • 7
  • 49
  • 73
Erich Kitzmueller
  • 36,381
  • 5
  • 80
  • 102
2

The HSSFRow has a method called setRowNum(int rowIndex).

When you have to "delete" a row, you put that index in a List. Then, when you get to the next row non-empty, you take an index from that list and set it calling setRowNum(), and remove the index from that list. (Or you can use a queue)

True Soft
  • 8,675
  • 6
  • 54
  • 83
1

My special case (it worked for me):

    //Various times to delete all the rows without units
    for (int j=0;j<7;j++) {
      //Follow all the rows to delete lines without units (and look for the TOTAL row)
      for (int i=1;i<sheet.getLastRowNum();i++) {
        //Starting on the 2nd row, ignoring first one
        row = sheet.getRow(i);
        cell = row.getCell(garMACode);
        if (cell != null) 
        {
          //Ignore empty rows (they have a "." on first column)
          if (cell.getStringCellValue().compareTo(".") != 0) {  
            if (cell.getStringCellValue().compareTo("TOTAL") == 0) {
              cell = row.getCell(garMAUnits+1);
              cell.setCellType(HSSFCell.CELL_TYPE_FORMULA);
              cell.setCellFormula("SUM(BB1" + ":BB" + (i - 1) + ")");
            } else {
              cell = row.getCell(garMAUnits);
              if (cell != null) {
                int valor = (int)(cell.getNumericCellValue());
                if (valor == 0 ) {
                  //sheet.removeRow(row);
                  removeRow(sheet,i);
                }
              }
            }
          }
        }
      }
    }
Divyesh Kanzariya
  • 3,629
  • 3
  • 43
  • 44
relos
  • 11
  • 1
1

This answer is an extension over AndreAY's answer, Giving you complete function on deleting a row.

public boolean deleteRow(String sheetName, String excelPath, int rowNo) throws IOException {

    XSSFWorkbook workbook = null;
    XSSFSheet sheet = null;

    try {
        FileInputStream file = new FileInputStream(new File(excelPath));
        workbook = new XSSFWorkbook(file);
        sheet = workbook.getSheet(sheetName);
        if (sheet == null) {
            return false;
        }
        int lastRowNum = sheet.getLastRowNum();
        if (rowNo >= 0 && rowNo < lastRowNum) {
            sheet.shiftRows(rowNo + 1, lastRowNum, -1);
        }
        if (rowNo == lastRowNum) {
            XSSFRow removingRow=sheet.getRow(rowNo);
            if(removingRow != null) {
                sheet.removeRow(removingRow);
            }
        }
        file.close();
        FileOutputStream outFile = new FileOutputStream(new File(excelPath));
        workbook.write(outFile);
        outFile.close();


    } catch(Exception e) {
        throw e;
    } finally {
        if(workbook != null)
            workbook.close();
    }
    return false;
}
Yan Khonski
  • 12,225
  • 15
  • 76
  • 114
Vishwanath gowda k
  • 1,675
  • 24
  • 26
  • How does your approach to saving the file fit with a situation where the file is accessed through a url e.g `InputStream file = new URL("http://docker:[portNumber]/fileName.xlsx").openStream();` – Larry Apr 08 '18 at 20:33
0

I'm trying to reach back into the depths of my brain for my POI-related experience from a year or two ago, but my first question would be: why do the rows need to be removed before parsing? Why don't you just catch the null result from the sheet.getRow(rowNum) call and move on?

delfuego
  • 14,085
  • 4
  • 39
  • 39
  • Because I already made a parser for Excel files that are formatted as a simple table (column names at the first row and data below) and I need to warn the user if there are empty rows. Also, the files that I want to import now are from another application and have a lot of extra rows (empty or not) only to make the file look nicer! – fmaste Dec 02 '09 at 19:23
  • OK -- I'd just say, though, that you'd probably be well-served just to subclass your existing parser and let your subclass ignore empty rows rather than do something with them; that way, the files from the other app can still look nice and you don't have to die on those files. :) – delfuego Dec 02 '09 at 19:27
  • Yes. I also think of that. But the file has things like: a row only for the first column (the id), and the rest of the columns in the row below. Just because it looks nicer. I need to make it a single row and delete one. I think that I would make a class containing a sheet and a list of row indexes and delete the indexes when I delete a row. Its to much, but I need to delete row, I don't understand why you cannot delete row with this library! – fmaste Dec 02 '09 at 19:36