背景
最近在开发一些对数据进行抓取、组织、导出的小工具,需要对Excel文件进行读取和写入操作。这次的小工具使用了COM组件里的Excel对象模型来实现Excel文件的读写,故此记录一下整个操作的过程,以便将来再次使用时可以参考。
首先简单整理一下能想到的读写Excel文件的几种方法:
- 将Excel文件作为数据源,使用ADO.NET连接Excel数据库进行读写操作。
- 使用COM组件里的Excel对象模型,以类似Excel应用程序的方式进行读写操作。
- 使用中间格式文件(如CSV、XML等文本文件格式)保存和操作数据。
- 利用第三方库读写Excel文件(不同的库有不同的实现方式,主流的是对Excel文件本身直接进行操作,以提高程序执行的速度)。
其实我最倾向于使用第3种方法来开发,因为如果直接读写文本文件则程序的执行效率最高,编写代码实现程序的时候学习成本也最低。但是现实情况是客户提需求时都会使用xlsx文件来存储输入数据以及输出模板,如果每次都要转成中间格式文件的话操作太繁琐,同时也会丢失很多xlsx文件才拥有的丰富特性。
由于我需要处理的数据总量并不多,也不需要大批量一次性写入数据,如果使用COM组件里的Excel对象模型方式来操作则性能已经够用,同时该种方式来操作Excel文件类似于使用Excel应用程序来处理表格,编程实现的速度更快(客户要的比较急,希望一下午就能搞定)。
下面就记录一下在C#.NET平台上使用COM组件里的Excel对象模型来进行简单的Excel读写操作的实现方法。
准备
新建一个C#.NET控制台应用程序(数据处理需要较长时间,如果做Forms应用的话还要开多线程,又要另外花时间处理多线程的问题),在项目的References里添加Microsoft Office和Microsoft Excel两个引用库。具体如下图所示,在Reference Manager对话框中选择「COM」标签卡,之后找到「Microsoft Office xx.x Object Library」和「Microsoft Excel xx.x Object Library」这两项并勾选,最后单击「确认」键完成添加。
添加完成后,项目的「References」列表里会增加「Microsoft.Office.Core」和「Microsoft.Office.Interop.Excel」两项,具体如下图所示。
最后在项目中需要对Excel文件进行操作的地方,使用该命名空间中的类即可。为了保持代码简洁,又不至于造成类名冲突,可以在cs文件开始的地方,使用缩短后的空间引用名。
using Excel = Microsoft.Office.Interop.Excel;
编程
一个完整的Excel操作过程主要涉及以下6个类,
- Application:代表的是Excel实例,就是整体的Excel程序。
- WorkBooks:是Wookbook的容器,管理Excel实例中所有打开的Workbook。
- WorkBook:实质意义上的某个Excel文件,新建、打开、保存操作都针对这个类进行。
- Worksheets:是Worksheet的容器,管理Excel文件中所有的Sheet表。
- Worksheet:实质意义上的一张Sheet表。
- Range:表中的一片单元格的区域,针对单元格的操作如读取、写入、设置等都在这个类里面。
下面记录一下通过编程来获取和写入Excel文件中指定单元格数据的示例代码。在编程过程中可以发现,使用COM组件来进行Excel操作的难点其实不在操作本身,而在于COM组件是非托管代码,何时进行垃圾回收、如何进行垃圾回收、垃圾回收是否执行到位等与程序核心功能无关的问题其实才是麻烦的点。所以在下面的示例代码中,还存在一些细节问题没有完全捋清,主要就是关于COM组件关闭和回收的部分,故示例代码虽然能实现预定功能,但是还存在BUG隐藏其中。
using System;
using Excel = Microsoft.Office.Interop.Excel;
namespace Test {
class Program {
static void Main( string[ ] args ) {
//文件路径列表
string[ ] strFiles = new string[ ] {
@".\MyFile_00.xlsx",
@".\MyFile_01.xlsx",
@".\MyFile_02.xlsx",
@".\MyFile_03.xlsx"
};
//声明可能用到的变量
Excel.Application excelApplication = null;
Excel.Workbooks excelWorkbooks = null;
Excel.Workbook excelWorkbook = null;
Excel.Worksheets excelWorksheets = null;
Excel.Worksheet excelWorksheet = null;
Excel.Range excelRange = null;
//读取和写入单元格数据
try {
//对Application进行初始化和设置
excelApplication = new Excel.Application( );
excelApplication.Visible = false; //Excel无前台界面
excelApplication.DisplayAlerts = false; //跳过警告显示
excelApplication.UserControl = false; //不允许界面操作
//根据文件路径,打开Workbook
excelWorkbooks = excelApplication.Workbooks;
excelWorkbook = excelWorkbooks.Open( strFiles[ 0 ] );
//根据索引号(从1开始编号),打开Worksheet
excelWorksheets = ( Excel.Worksheets )excelWorkbook.Worksheets;
excelWorksheet = excelWorksheets[ 1 ];
//根据行号和列号(从1开始编号),读取和写入单元格
string strValue = "";
excelRange = excelWorksheet.Cells[ 1, 1 ];
strValue = Convert.ToString( excelRange.Value );
Console.WriteLine( strValue );
excelRange = excelWorksheet.Cells[ 1, 2 ];
excelRange.Value = strValue;
//保存Workbook
excelWorkbook.Save( );
//释放资源
excelWorkbook.Close( );
excelWorkbooks.Close( );
excelApplication.Quit( );
System.Runtime.InteropServices.Marshal.ReleaseComObject( excelRange );
System.Runtime.InteropServices.Marshal.ReleaseComObject( excelWorksheet );
System.Runtime.InteropServices.Marshal.ReleaseComObject( excelWorksheets );
System.Runtime.InteropServices.Marshal.ReleaseComObject( excelWorkbook );
System.Runtime.InteropServices.Marshal.ReleaseComObject( excelWorkbooks );
System.Runtime.InteropServices.Marshal.ReleaseComObject( excelApplication );
excelRange = null;
excelWorksheet = null;
excelWorksheets = null;
excelWorkbook = null;
excelWorkbooks = null;
excelApplication = null;
}
catch( Exception ex ) {
Console.WriteLine( ex.Message );
}
}
}
}
施工中。。。。。。
转载请注明出处。