Interop Excel速度缓慢

7

我正在编写一个应用程序,用于打开Excel表格并读取其内容。

MyApp = new Excel.Application();
MyBook = MyApp.Workbooks.Open(filename);
MySheet = (Excel.Worksheet)MyBook.Sheets[1]; // Explict cast is not required here
lastRow = MySheet.Cells.SpecialCells(Excel.XlCellType.xlCellTypeLastCell).Row;
MyApp.Visible = false;

这个过程需要大约6-7秒钟,这在Interop Excel中正常吗?

还有比这更快的读取Excel的方法吗?

string[] xx = new string[lastRow];
for (int index = 1; index <= lastRow; index++)
{
   int maxCol = endCol - startCol;
   for (int j = 1; j <= maxCol; j++)
   {
      try
      {
         xx[index - 1] += (MySheet.Cells[index, j] as Excel.Range).Value2.ToString();
      }
      catch
      {    
      }

      if (j != maxCol) xx[index - 1] += "|";
   }
}
MyApp.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(MySheet);
System.Runtime.InteropServices.Marshal.ReleaseComObject(MyBook);
System.Runtime.InteropServices.Marshal.ReleaseComObject(MyApp);

1
你的Excel有多大? - Watsche
4个回答

5

在@RvdK的回答上追加 - 是的,COM互操作很慢。

为什么会慢?

这是由于它的工作方式。.NET中的每个调用都必须从本地COM代理进行转换,然后必须从一个进程(您的应用程序)通过IPC传递到COM服务器(Excel)(通过Windows内核内的IPC),然后将其翻译(分派)从服务器的本地代理到本机代码,其中参数从OLE自动化兼容类型转换为本机类型,检查其有效性并执行函数。函数的结果通过两个不同进程之间的几层传输大约相同的方式返回。

因此,每个命令的执行成本相当高,您做得越多,整个过程就越慢。您可以在Web上找到大量文档,因为COM是旧的且运行良好的标准(与Visual Basic 6一起消失)。

这样的文章示例之一在此处:http://www.codeproject.com/Articles/990/Understanding-Classic-COM-Interoperability-With-NE

有更快的读取方法吗?

  1. ClosedXML可以使用Microsoft的OpenXml SDK读取和写入Excel xlsx文件(甚至包括公式、格式等),请参见此处:https://closedxml.codeplex.com/wikipage?title=Finding%20and%20extracting%20the%20data&referringTitle=Documentation

  2. Excel数据阅读器声称能够读取旧版和新版Excel数据文件,我自己没有尝试过,请看这里:https://exceldatareader.codeplex.com/

  3. 另一种更快速地读取数据的方法是使用Excel自动化将工作表转换为您可以轻松理解并且不需要Interop层批处理的数据文件(例如XML、CSV)。这个答案展示了如何做到这一点。


1
这绝对值得更多的赞。这证实了我对使用Interop程序集与COM工作流程的一些怀疑。我现在知道为什么88.6%的运行时间都花费在对Excel的这些调用上了。在这种情况下跟踪性能问题并不容易。 - Anthony Mason
1
你会认为像微软这样的巨型公司 - 特别是考虑到读写Excel文件的需求 - 可以承担生产比以下更好的工具:(1)笨重的COM(2)破坏数据并提供很少导入控制的Db驱动程序(3)晦涩难懂的OpenXML SDK。他们真的应该感到羞愧。 - B H

3
这个答案只涉及你问题的第二部分。 你在那里使用了许多不正确的范围,这是不合适的,而且非常慢。
首先读取完整的范围,然后像这样迭代结果:
var xx[,] = (MySheet.Cells["A1", "XX100"] as Excel.Range).Value2;
for (int i=0;i<xx.getLength(0);i++)
{
    for (int j=0;j<xx.getLength(1);j++)
    {
         Console.WriteLine(xx[i,j].toString());
    }
}

这将会快很多!


1
我同意将整个范围读入2D数组,但请不要仅仅为了展示结果而这样迭代... 将该2D数组转换为可枚举列表,一次性打印出所有内容... - user2140173

3

3
不是对我回答...为什么这很慢?也许你没用好?使用COM读取Excel文件并进行迭代听起来对我来说像是个糟糕的想法。Ranges是二维数组,你将整个范围“导入”到一个数组中,然后再处理它..如果你还在迭代,使用第三方库也不会有太大改进.... - user2140173
我同意这不应该是一个“答案”。但我喜欢EPPlus。快速且易于使用。 - mason

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接