【问题标题】:Microsoft.Office.Interop.Excel really slowMicrosoft.Office.Interop.Excel 真的很慢
【发布时间】:2010-10-21 15:28:40
【问题描述】:

我正在使用标准 Microsoft.Office.Interop.Excel 将 1200 X 800 矩阵 (indexMatrix) 导出到 excel 文件。该应用程序有效,只是它真的真的很慢(即使对于 100 x 100 矩阵)。我还通过 TextWriter 导出文本文件,它几乎可以立即运行。有什么方法可以更快地导出到excel文件?

这是我的代码:

        Excel.Application xlApp=new Excel.Application();
        Excel.Workbook xlWorkBook;
        Excel.Worksheet xlWorkSheet;
        object misValue = System.Reflection.Missing.Value;

        //xlApp = new Excel.ApplicationClass();
        xlWorkBook = xlApp.Workbooks.Add(misValue);

        xlWorkSheet = (Excel.Worksheet)xlWorkBook.Worksheets.get_Item(1);
        for (int i = 0; i < 800; i++)   //h
            for (int j = 0; j < 1200; j++)
                xlWorkSheet.Cells[i+1,j+1] =indexMatrix[i][j];


        xlWorkBook.SaveAs("C:\\a.xls", Excel.XlFileFormat.xlWorkbookNormal, misValue, misValue, misValue, misValue, Excel.XlSaveAsAccessMode.xlExclusive, misValue, misValue, misValue, misValue, misValue);
        xlWorkBook.Close(true, misValue, misValue);
        xlApp.Quit();

        releaseObject(xlWorkSheet);
        releaseObject(xlWorkBook);
        releaseObject(xlApp);

        MessageBox.Show("Excel file created , you can find the file c:\\csharp-Excel.xls");

【问题讨论】:

    标签: c# excel interop


    【解决方案1】:

    您正在更新单个单元格。这将非常缓慢。如果您考虑一下,每次更新单元格时,都会将 RPC 调用编组到 Excel 进程。

    如果您在单个语句(一个跨进程调用)中将二维数组值分配给相同维度的 Excel 范围,而不是当前的 1200 x 800,速度会大大 = 960,000 次跨进程调用。

    类似:

    // Get dimensions of the 2-d array
    int rowCount = indexMatrix.GetLength(0);
    int columnCount = indexMatrix.GetLength(1);
    // Get an Excel Range of the same dimensions
    Excel.Range range = (Excel.Range) xlWorkSheet.Cells[1,1];
    range = range.get_Resize(rowCount, columnCount);
    // Assign the 2-d array to the Excel Range
    range.set_Value(Excel.XlRangeValueDataType.xlRangeValueDefault, indexMatrix);
    

    实际上,为了迂腐,上面的代码中有三个跨进程调用(.Cells、.get_Resize 和 .set_Value),并且您的代码中每次迭代有两个调用(.Cells get 和一个隐式 .set_Value ) 总共 1200 x 800 x 2 = 1,920,000。

    注意 range.get_Resizerange.set_Value 是我首次撰写本文时使用的旧版 Excel 互操作库所必需的。这些天您可以使用range.Resizerange.Value,正如@The1nk 的评论中所述。

    【讨论】:

    • 您的代码没有显示它是什么类型。它应该是一个对象数组,即object[,]
    • 您需要将数据从锯齿状数组 (int[][]) 复制到矩形数组:object[,] 或者 int[,] 也可以。
    • 漂亮的解决方案,对我的 44,279 个细胞非常有效。从 5 分钟到 3 秒。给未来访客的一张便条 - 您无需使用 get_Resizeset_Value。您只需照常使用cell.Resizecell.Value
    • "如果你仔细想想,每次更新单元格时,都会将一个 RPC 调用编组到 Excel 进程。"即使你不去想它也可能会发生。
    • 谢谢,但给HRESULT: 0x800A03EC 例外。
    【解决方案2】:

    Excel 互操作永远不会很快。您基本上是在远程控制 Excel 应用程序的一个实例。通过创建 CSV 文件然后使用 Excel 互操作将其转换为 .xls 或 .xlsx 文件,您可能会取得更大的成功

    【讨论】:

    • +1 不错的方法,这也确保更快,因为 CSV 文件更可能是文本文件,因此 TextWriter 类可以处理它。
    • 是的,这也是我的首选方法。尽管当单元格值包含换行符时您会遇到问题,因为这会破坏您的行索引。有人对这个问题有什么建议吗?
    • 回复我上面的评论:使用 import-csv (powershell) 或等效的库函数来处理字段值中使用的限定符字符和换行符的混乱情况。
    【解决方案3】:

    我在读取一个非常大的 excel 文件时遇到了类似的问题,并且使用互操作花费了 2 多个小时。

    我尝试使用 ClosedXml,整个过程不到 10 秒。 ClosedXml

    // To loop 
    Sheet.Row(y).Cell(x).Value
    

    另外请记住,除非您安装了 excel,否则互操作将无法在您的服务器上运行。 ClosedXml 不需要安装 excel。

    【讨论】:

    • 第一次使用 ClosedXML,它真的很快。我将 InsertData 与一组数组和一个参数一起使用。与 POI 的 HSSF 和 HSSF 疯狂以及互操作的 COM Exceptions 相比非常流畅!!! +1
    • ClosedXML 更快,因为它使用 OpenXML,这意味着它直接写入文件。没有中间 API,没有要更新的 UI 等。如果它是您的用例的一个选项,它可能总是最快的选项,您可以使用普通的 OpenXML 来做同样的事情。跨度>
    • 我的 2 美分。使用 Interop:39 秒,使用 ClosedXml:0.7 秒。
    【解决方案4】:

    在写入任何数据之前关闭ScreenUpdatingApplication.ScreenUpdating = FALSE然后在代码末尾打开= TRUE

    【讨论】:

      【解决方案5】:

      使用 Value2 使其快速; 填写数据前显示excel

      【讨论】:

        【解决方案6】:

        ClosedXML 是一个奇迹,它更快更容易使用。

        var workbook = new XLWorkbook();//create the new book
        
        var worksheet = workbook.Worksheets.Add("Computer Install");// Add a sheet
        worksheet.Cell(1,1).Value = "PC Name";// (Row, column) write to a cell
        
        workbook.SaveAs(@"LIC documents.xlsx");// Save the book
        

        您使用 nu Get 软件包进行安装。 https://www.nuget.org/packages/ClosedXML

        【讨论】:

        • 是的,令人印象深刻。从大约 4.5 小时到 5 分钟。 Github:github.com/closedxml/closedxml
        • ClosedXML 更快,因为它使用 OpenXML,这意味着它直接写入文件。没有中间 API,没有要更新的 UI 等。如果它是您的用例的一个选项,它可能总是最快的选项,您可以使用普通的 OpenXML 来做同样的事情。跨度>
        【解决方案7】:

        有三种方法可以做到这一点,其他人在不同的答案中提到了其中两种:

        1. 直接将excel中某个范围的值设置为二维数组。
        2. 将数据写入 CSV 文件,然后使用互操作将 CSV 文件保存为 xls 或 xlsx 文件。
        3. 将数据写入 CSV 文件,然后使用数据连接功能将 CSV 用作数据源并导入数据。

        以上三种方法都非常快。我可以在大约 6 秒内写入 90000 行和 100 列的数据。

        附:然而,他们并没有解决我格式化边框、字体样式、颜色、单元格合并等数据的问题。

        【讨论】:

          【解决方案8】:

          抱歉,我回答有点晚了。我正在处理我的项目,我们不得不使用互操作 Excel。而且我们的数据太大,使用 interop excel 需要超过 1 分钟。我们尝试了其他方法,即将 datagridview 的所有内容复制到剪贴板,使用 interop excel 打开一个 excel 工作表,然后将内容粘贴到 excel。只需不到 1 秒,即可完美导出我们的数据。

          DataGridView 转字符串:

              var newline = System.Environment.NewLine;
              var tab = "\t";
              var clipboard_string = "";
          
              foreach (DataGridViewRow row in dgProjeler.Rows)
              {
                  for (int i = 0; i < row.Cells.Count; i++)
                  {
                      if (i == (row.Cells.Count - 1))
                          clipboard_string += row.Cells[i].Value + newline;
                      else
                          clipboard_string += row.Cells[i].Value + tab;
                  }
              }
          

          并将字符串复制到剪贴板

                  Clipboard.SetText(clipboard_string);
          

          然后打开一个工作表,粘贴内容。

                  Excel.Application app = new Excel.Application();
                  app.Visible = true;
                  Excel.Workbook wb = app.Workbooks.Add(1);
                  Excel.Worksheet ws = (Excel.Worksheet)wb.Worksheets[1];
                  // changing the name of active sheet
                  ws.Name = "Exported from gridview";
          
                  ws.Rows.HorizontalAlignment = HorizontalAlignment.Center;
                  app.ActiveWindow.Activate();
          
          
                  ws.Activate();
                  ws.Paste();
          
                  ws.Cells.EntireColumn.AutoFit();
          

          它对我很有效,我希望它可以帮助那些仍然找不到解决方案的人。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2017-11-10
            • 2010-12-28
            • 2013-01-25
            • 2012-01-30
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多