2011-03-21 102 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(); 
         }