2011-03-21 107 views
14

我有用户提供的需要转换为PDF的excel文件。使用excel interop,我可以用.ExportAsFixedFormat()来做到这一点。当工作簿拥有数百万行时,我的问题就出现了。这变成了一个有50k +页面的文件。如果工作簿包含所有这些行的内容,那就没问题了。每次有这些文件中的一个出现时,大概有50行有内容,其余都是空白的。我怎样才能去除空行,以便我可以将它导出为体面大小的PDF?使用Excel Interop删除空行

  1. 我试着从末端开始行,一个接一个,使用​​检查,如果该行有内容,如果是的话,将其删除。这不仅需要永久,这似乎失败后约100k行,并出现以下错误:

    无法评估表达式,因为代码已优化或本机帧位于调用堆栈之上。

  2. 我试过使用SpecialCells(XlCellType.xlCellTypeLastCell, XlSpecialCellsValue.xlTextValues)但包括一行,如果任何单元格格式(如bg颜色)。

  3. 我试过使用Worksheet.UsedRange然后删除之后的所有内容,但UsedRange与第二点有相同的问题。


这是我试过的代码:

for (int i = 0; i < worksheets.Count; i++) 
{ 
    sheet = worksheets[i + 1]; 
    rows = sheet.Rows; 
    currentRowIndex = rows.Count; 
    bool contentFound = false; 

    while (!contentFound && currentRowIndex > 0) 
    { 
     currentRow = rows[currentRowIndex]; 

     if (Application.WorksheetFunction.CountA(currentRow) == 0) 
     { 
      currentRow.Delete(); 
     } 
     else 
     { 
      contentFound = true; 
     } 

     Marshal.FinalReleaseComObject(currentRow); 
     currentRowIndex--; 
    } 

    Marshal.FinalReleaseComObject(rows); 
    Marshal.FinalReleaseComObject(sheet); 
} 

for (int i = 0; i < worksheets.Count; i++) 
{ 
    sheet = worksheets[i + 1]; 
    rows = sheet.Rows; 

    lastCell = rows.SpecialCells(XlCellType.xlCellTypeLastCell, XlSpecialCellsValue.xlTextValues); 
    int startRow = lastCell.Row; 

    Range range = sheet.get_Range(lastCell.get_Address(RowAbsolute: startRow)); 
    range.Delete(); 

    Marshal.FinalReleaseComObject(range); 
    Marshal.FinalReleaseComObject(lastCell); 
    Marshal.FinalReleaseComObject(rows); 
    Marshal.FinalReleaseComObject(sheet); 
} 

难道我有一个问题,我的代码,这是一个互操作问题或者它只是一个Excel可以做什么的限制?有没有更好的方法来做我正在尝试的?

+0

我真的很想看看这个话题。你有一个演示文件来进行测试吗? – 2015-07-04 17:19:37

+0

@PilgerstorferFranz对不起,我没有。这个项目早已不复存在。 – Chris 2015-07-04 23:32:42

+0

你找到解决方案吗? – 2015-07-05 04:26:13

回答

0

您是否尝试过Sheet1.Range("A1").CurrentRegion.ExportAsFixedFormat()其中Sheet1是有效的工作表名称,“A1”是您可以测试的单元格以确保它位于要导出的范围内?

问题仍然存在,为什么Excel会认为这些“空”单元中存在数据?格式化?需要清除的预先存在的打印区域?我知道我以前遇到过这样的情况,这是现在想到的唯一可能性。

+0

但是,这也不起作用,我的问题与我的第二点和第三点相同,如果我可以告诉用户不要制作荒谬的电子表格,那将是一件好事:D – Chris 2011-04-04 19:19:12

0

尝试这些步骤: -

  1. 拷贝Worksheet.UsedRange到一个单独的片材(Sheet 2中)。
  2. 使用特殊粘贴,这样格式保留
  3. 尝试解析Sheet2中未使用的行

如果这并不帮助尝试重复步骤2格式的信息被清除,然后解析Sheet2中。你可以随时复制后格式化信息(如果他们足够简单)

+0

我试过了第一部分你提出的问题与第二点和第三点相同问题我没有尝试没有格式化的复制,然后重新应用格式化,如何做到这一点?*如果它们足够简单* - 是否意味着复制格式不会永远是一个可行的选择?因为这些是用户提供的工作表,我不能保证他们将有什么格式。 – Chris 2011-04-04 19:18:51

0

如果你可以先加载Excel文件到通过OleDBAdapter一个DataSet,这是比较容易的进口删除空行...... 试试这个OleDBAdapter Excel QA我通过堆栈溢出发布。

然后将DataSet导出到新的Excel文件并将该文件转换为PDF。当然,这可能是一个很大的“IF”,具体取决于Excel的布局(或缺乏)。

+0

我没有使用数据集。我需要修改实际的Excel文件,它看起来像ADO.NET [不支持'delete'操作](http://support.microsoft.com/kb/316934) – Chris 2011-05-05 16:38:36

+0

呵呵,我不应该假设你使用的是oledbadapter和DataSet,我将修改我的答案 – 2011-05-05 19:04:19

0

我今天必须解决这个问题,以寻找可能的案例的一个子集。

如果您的电子表格符合下列条件:1行

    1. 数据中的所有列有标题的文字与数据的所有行的序列,直到第一个空行。

    接着,下面的代码可能会有所帮助:

    private static string[,] LoadCellData(Excel.Application excel, dynamic sheet) 
        { 
         int countCols = CountColsToFirstBlank(excel, sheet); 
         int countRows = CountRowsToFirstBlank(excel, sheet); 
         cellData = new string[countCols, countRows]; 
         string datum; 
    
         for (int i = 0; i < countCols; i++) 
         { 
          for (int j = 0; j < countRows; j++) 
          { 
           try 
           { 
            if (null != sheet.Cells[i + 1, j + 1].Value) 
            { 
             datum = excel.Cells[i + 1, j + 1].Value.ToString(); 
             cellData[i, j] = datum; 
            } 
           } 
           catch (Exception ex) 
           { 
            lastException = ex; 
            //Console.WriteLine(String.Format("LoadCellData [{1}, {2}] reported an error: [{0}]", ex.Message, i, j)); 
           } 
          } 
         } 
    
         return cellData; 
        } 
    
        private static int CountRowsToFirstBlank(Excel.Application excel, dynamic sheet) 
        { 
         int count = 0; 
    
         for (int j = 0; j < sheet.UsedRange.Rows.Count; j++) 
         { 
          if (IsBlankRow(excel, sheet, j + 1)) 
           break; 
    
          count++; 
         } 
         return count; 
        } 
        private static int CountColsToFirstBlank(Excel.Application excel, dynamic sheet) 
        { 
         int count = 0; 
    
         for (int i = 0; i < sheet.UsedRange.Columns.Count; i++) 
         { 
          if (IsBlankCol(excel, sheet, i + 1)) 
           break; 
    
          count++; 
         } 
         return count; 
        } 
    
        private static bool IsBlankCol(Excel.Application excel, dynamic sheet, int col) 
        { 
         for (int i = 0; i < sheet.UsedRange.Rows.Count; i++) 
         { 
          if (null != sheet.Cells[i + 1, col].Value) 
          { 
           return false; 
          } 
         } 
    
         return true; 
        } 
        private static bool IsBlankRow(Excel.Application excel, dynamic sheet, int row) 
        { 
         for (int i = 0; i < sheet.UsedRange.Columns.Count; i++) 
         { 
          if (null != sheet.Cells[i + 1, row].Value) 
          { 
           return false; 
          } 
         } 
    
         return true; 
        } 
    
  • +0

    I不相信这是一个工作因为(如问题中所述)**具有格式的空单元格**不应被删除。除非我弄错了,否则你的代码段会错误地删除这些行,因为这些值是'null',而格式可能是为了保留。 – gravity 2016-06-24 16:06:39

    -1

    请尝试以下代码:

    for (int i = 0; i < worksheets.Count; i++) 
    { 
        sheet = worksheets[i + 1]; 
        sheet.Columns("A:A").SpecialCells(XlCellType.xlCellTypeBlanks).EntireRow.Delete 
        sheet.Rows("1:1").SpecialCells(XlCellType.xlCellTypeBlanks).EntireColumn.Delete 
        Marshal.FinalReleaseComObject(sheet); 
    } 
    
    0

    我建议你得到包含某些值的行数,使用CountA(正如您在第1点中尝试的那样)。然后将这些行复制到新工作表中并从那里导出。将几行复制到新工作表并对其进行处理会更容易,而不是试图从源工作表中删除大量行。

    用于创建新的片材和复制的行可以用下面的代码:

     excel.Worksheet tempSheet = workbook.Worksheets.Add(); 
         tempSheet.Name = sheetName; 
         workbook.Save(); 
    

    //创建拷贝新行的新方法

    //作为rowIndex位置可以传递的总无你已经发现使用CountA的行数

    public void CopyRows(excel.Workbook workbook, string sourceSheetName, string DestSheetName, int rowIndex) 
         { 
          excel.Worksheet sourceSheet = (excel.Worksheet)workbook.Sheets[sourceSheetName]; 
          excel.Range source = (excel.Range)sourceSheet.Range["A" + rowIndex.ToString(), Type.Missing].EntireRow; 
    
          excel.Worksheet destSheet = (excel.Worksheet)workbook.Sheets[DestSheetName]; 
          excel.Range dest = (excel.Range)destSheet.Range["A" + rowIndex.ToString(), Type.Missing].EntireRow; 
          source.Copy(dest); 
    
          excel.Range newRow = (excel.Range)destSheet.Rows[rowIndex+1]; 
          newRow.Insert(); 
          workbook.Save(); 
         }