背景:
最近用C#写了一个小工具需要操作Excel,网上搜到的方案都是COM组件方式,即Microsoft.Office.Interop.Excel居多,除此之外,还有以下几种方法:
Microsoft.Office.Tools.Excel网上较少介绍,资料和例子有限,但好像是比COM方法先进OleDB方式,即把Excel当作一个数据库采用NPOI 库方式这种方法无需在目标电脑上安装ExcelCSV方式,也就是作为文件流来操作OpenXML方式,貌似也是第一个第三方工具
由于时间关系,采用了资料最多的COM组件方式,即Microsoft.Office.Interop.Excel,但在实际使用中遇到了几个障碍,在此记录一下。
不过,这些故障的原因都没有官方权威说明背书,所以可能有误,请自行甄别。如有高手能够不吝赐教,万分感谢!
用COM组件方法时,作为宇宙第一IDE的Visual Studio 无法进行“编辑代码并继续调试”。这个功能的缺失,大大降低了本人懒癌晚期的开发效率。好处就是需要在F5之前尽量把代码想清楚,而不像以前那样,把所有的工作就交给调试器Range.FindNext()方法,不可以嵌套使用,否则会造成下一个查找开始点的混乱,从而造成死锁。我的需求有点特别。因为我要查找的字符串在Excel中被拆分成一行n列,所以需要先找到包含该字符串第一部分的某个单元格,然后在该行上对比随后的几列加起来是不是要查找的字符串。说起来麻烦,下面是例子。我要查找的是191/1/1/1,到了Excel中是像下面这样储存的。
一开始我的想法是,先把整个Sheet中UsedRanged定义为rg,用rg.Find()方法先找到191,然后把该行定义为一个新的Range比如rg1,然后用rg1的Find()方法,查找后面的“1”,“1/1”。
其实到此为止都没有问题,问题出现在rg.FindNext()这个方法上。
rg.FindNext()方法需要的参数是rg本身,而查找过程的终止是对比当前所在Range的Address和事先保存好的第一次进行Find()的Address,如果两者一致,就说明FindNext回到了原点,当前查找范围不包含待查找内容,循环结束。
不过,像我上面的需求,因为有嵌套存在,就影响了FindNext的结果。
我其实想到过这个问题,所以我以为我重新定义了rg1,在rg1中进行Find是不会影响rg这个Range的FindNext动作的。可惜,事实证明并非如此,rg的FindNext因为受到影响,陷入无限循环,直到宇宙的终结……
没那么夸张,停止调试、杀死进程或者断电都行。(抱歉戏精上身了)
我暂时没有进一步考虑聪明点的办法,只是简单应用Sheet.Cells[m,n]这个方法取到了后面几个单元格进行对比,避免触碰Range.Find雷区。代价就是似乎效能降低了不少,而且需要把m和n进行硬编码,万一列数发生变化就要修改代码。
注意程序的最后对Excel进程的释放,否则任务管理器里会看到越来越多的Excel进程留在那里
关于这一点,可以参照下面两篇文章。
[C# .Net] 使用 Microsoft.Office.Interop.Excel 讀 / 寫 Excel | 余小章 @ 大內殿堂.tw透過.NET程式操作Excel的注意事項-黑暗執行緒关于Range的操作不太清楚网上的一些文章为什么固执地使用“A1”这样的参数来解释Range的范围,这种方式只适合硬编码,而无法用for循环来进行遍历。我觉得Cells[m,n]这种方式更适合循环操作。关于使用单元格的内容,网上的资料不是太多,一般都是判断Value2,或者把它再转成String。但有时候Value2会是null,而且Text这个属性也不是每次都很保险。有关效率问题,刚刚读到了下面这篇文章,貌似这样也避免了Value2/Text为null的异常情况。C#(com组件)操作Excel读写 - 红烧狮子头 - 博客园
和他所引用的文章
关于通过COM自动化调用Excel的效率问题 - 马维峰 - 博客园
我也要回头重写自己的查找/判断部分,提高一点性能。
上面这篇文章的内容值得一看。
暂时先想到这些,回头如果有时间了,会尝试一下读写Excel的另外几种方法。