C++操作Excel学习笔记
一:
【当前博文转载自http://blog.csdn.net/fullsail/article/details/4067416】
C++读取Excel文件方式比较
C++读取Excel的XLS文件的方法有很多,但是也许就是因为方法太多,大家在选择的时候会很疑惑。
由于前两天要做导表工具,比较了常用的方法,总结一下写个短文,
1.OLE的方式
这个大约是最常用的方式,这个方式其实启动了一个EXCEL的进程在背后读写EXCEL文件,这个方式的最大好处是什么事情都能做。包括设置EXCEL的格式,增加删除Sheet,读写单元格,等等。功能几乎是最全的,而且使用起来也不是特别的难。
其基本方法都是使用导出的.h文件进行OLE操作,但是由于OLE的接口说明文档不多,想非常完美的使用她们也不是太容易,好在例子也很多。
网上普遍认为OLE速度慢,EXCEL的OLE读写方式也基本一样。但是读取速度可以改进,如果在读取的加载整个Sheet的Range的全部数据,而不是一个个单元格读取,那么速度还是相对不错。想想原理也很简单,整体读取减少了OLE的交互次数。OLE的写入方式一般只能几个进行比较方便,所以速度可能要快很多。
我自己的亲身体会是,一个EXCEL文件,100多列的字段,如果采用一个个单元格的读取方式,1s大约3条左右的记录,如果整体读取,速度可以提高几十倍。
OLE读写EXCEL方式功能很强大,读取速度还可以,但写入速度不高,当然这个方式不可能移植的,而且你必须安装了EXCEL。
2.Basic EXCEL 方式
这是CodeProject上的一个推荐开源工程了,
http://www.codeproject.com/KB/office/BasicExcel.aspx
作者是基于EXCEL的文件格式进行的处理。但是为什么叫Basic EXCEL呢。
他不支持很多东西,公式,文件格式,表格合并等(有人说中文支持也不好),所以可以认为他只支持最基本的EXCEL表格,
我自己的尝试是如果这个EXCEL文件有其他元素(公式,格式等),使用Basic EXCEL读取会失败。
OLE读写EXCEL方式功能比较弱,由于是直接根据文件格式操作,读写速度都不错,你也不需要按照EXCEL,另外这个方式是可以移植的,但是有一些成本,其代码比较晦涩难懂,而且没有注释,另外即使在Windows平台上,告警也很多。
3.Sourceforge 上的几个EXCEL库。
Sourceforge 上有几个开源的的EXCEL库,但是完善的不多,有的是为了PHP读写EXCEL准备的,包括libXLS,XLSlib,SmartEXCEL等。我下载了几个实验了一下,在Widonws都没有编译成功。也罢了。
4.ODBC的方式
这个亲身没有尝试过,但是按照原理,应该只能读写。
速度吗,ODBC的速度本来就是出名的慢了。
http://www.vckbase.com/document/viewdoc/?id=421
值得一提的是Basic EXCEL的作者原来在CodeProject上有一个给予ODBC方式的封装CSpreadSheet 。如果有兴趣,大家可以去看看。其实内心还是很佩服这个作者的。
http://www.codeproject.com/KB/database/cspreadsheet.aspx
5.ADO的方式
ADO的方式听说应该就是使用OLEDB的方式。和OLE的方式应该没有本质区别。我看了看例子也和OLE很像
6.LibXL
LibXL 是一个收费的EXCEL的库。
按照他的说明,他可以不依赖EXCEL读取XLS文件。包括设置格式等。看例子操作应该很简单。但是是否可以移植到Linux平台,我估计难度也不小。呵呵。
由于要收费,没有法子测试了。
7.网上一些号称不用OLE读取EXCEL例子
初步看了一下,这个应该是网上探索EXCEL格式文档的例子。可以实际操作的方式不强。
二:
【当前博文转载自http://www.byywee.com/page/M0/S599/599654.html】
VS2010 C++ 操纵Excel表格的编程实现
经由过程VC实现对Excel表格的操纵的办法有多种,如:经由过程ODBC数据库实现,经由过程解析Excel表格文件,经由过程OLE/COM的实现。本文首要研究经由过程OLE/COM实现对Excel表格的操纵。
本文源码的应用景象申明:
Windows XP SP3
Microsoft Visual Studio 2010
Microsoft Office Excel 2007
1、添加OLE/COM支撑。
起首,应用法度必须添加对OLE/COM的支撑,才干导入OLE/COM组件。
本文应用的是MFC对话框法度,在创建工程的领导中选中Automation选项即可为法度主动添加响应的头文件和OLE库初始化代码。
经由过程查看源代码,可以知道在stdafx.h的头文件中,添加了OLE/COM很多类所需添加的头文件。
#include // MFC 主动化类
同时,在应用法度类的InitInstance函数中,添加了OLE/COM的初始化代码,如下所示:
// 初始化 OLE 库
if (!AfxOleInit())
{
AfxMessageBox(IDP_OLE_INIT_FAILED);
return FALSE;
}
2、导入并封装Excel中的接口
Excel作为OLE/COM库插件,定义好了各类交互的接口,这些接口是跨说话的接口。VC可以经由过程导入这些接口,并经由过程接口来对Excel的操纵。
因为本文只关怀对Excel表格中的数据的读取,首要存眷几个_Application、Workbooks、_Workbook、Worksheets、_Worksheet、Range等几个接口。Excel的各类接口的属性、办法可以经由过程MSDN的Office Development进行查询。
VS2010导入OLE/COM组件的接口的步调为:Project->Class Wizard->Add Class->MFC Class From TypeLib,先选择要导入的组件地点的路径,即Excel.exe地点的路径,然后再选择要导入的Excel类型库中的接口。
在完成接口导入后,VS2010将主动为导入的接口创建响应的实现类,用于对接口属性和办法的实现。因为标准的C++没有属性接见器,只能添加一个两个存取函数来实现对属性的接见,经由过程在属性名称前加上get_和put_前缀分别实现对属性的读写操纵。即,由VC主动完成C++类对接口的封装。
文所导入的接口对应的类和头文件的申明如下所示:
Excel接口 | 导入类 | 头文件 | 申明 |
_Application | CApplicaton | Application.h | Excel应用法度。 |
Workbooks | CWorkbooks | Workbooks.h | 工作簿的容器,里面包含了Excel应用法度打开的所有工作簿。 |
_Workbook | CWorkbook | Workbook.h | 单个工作簿。 |
Worksheets | CWorksheets | Worksheets.h | 单个工作簿中的Sheet表格的容器,包含该工作簿中的所有Sheet。 |
_Worksheet | CWorksheet | Worksheet.h | 单个Sheet表格。 |
Range | CRange | Range.h | 必然命量的单位格,可对单位格进行单个或多个单位格进行操纵。 |
3、导入Excel的全部类型库
接口对应类只是对接口的属性和办法进行了封装,而Excel中的数据类型,如列举类型却并为并不克不及应用,是,为了更便利的操纵Excel,还须要导入Excel的数据类型。
经由过程查看导入接口对应的头文件可以发明,在所有导入接口的头文件中,都邑有这么行:
#import "D:\\Program Files\\Microsoft Office\\Office12\\EXCEL.EXE" no_namespace
这行代码的感化是导入Excel全部类型库到工程中。
由VS2010主动产生的导入代码存在以下几个题目:
(1)若是导入了多个接口,每个头文件都邑把类型库导入一次,若是引用多个头文件,会导致类型库反复导入。
(2)Excel类型库中有些类型会跟MFC类库的某些类型冲突。
(3)Excel类型库的某些类型跟其他Office和VB的某些库相干,若是不导入相干库,将导致这些类型无法应用。。
以上三点题目的解决办法如下:
(1)仅在_Application接口对应头文件中导入Excel类型库。
(2)对冲突的类型进行重定名。
(3)在导入Excel类型库之前,先导入Office和VB的相干库。
更改后的导入类型库的代码如下:
#import "C:\\Program Files\\Common Files\\Microsoft Shared\\OFFICE12\\MSO.DLL" \
rename("RGB", "MSORGB") \
rename("DocumentProperties", "MSODocumentProperties")
using namespace Office;
#import "C:\\Program Files\\Common Files\\Microsoft Shared\\VBA\\VBA6\\VBE6EXT.OLB"
using namespace VBIDE;
#import "D:\\Program Files\\Microsoft Office\\Office12\\EXCEL.EXE" \
rename("DialogBox", "ExcelDialogBox") \
rename("RGB", "ExcelRGB") \
rename("CopyFile", "ExcelCopyFile") \
rename("ReplaceText", "ExcelReplaceText") \
no_auto_exclude
Using namesapce Excel;
编译法度后,会在Debug或Release目次下生成三个文件mso.tlh、vbe6ext.tlh和excel.tlh。经由过程打开文件可知,该三个文件的定名空间分别是Office、VBIDE和Excel。导入了Excel的全部类型库后,就可以应用Excel中的所有类型了。
4、操纵Excel步调
操纵Excel的首要步调如下:
(1)创建一个Excel应用法度。
(2)获得Workbook的容器。
(3)打开一个Workbook或者创建一个Workbook。
(4)获得Workbook中的Worksheet的容器。
(5)打开一个Worksheet或者创建一个WorkSheet。
(6)经由过程Range对WorkSheet中的单位格进行读写操纵。
(7)保存Excel。
(8)开释资料。
5、批量处理惩罚Excel表格
VC经由过程OLE/COM操纵Excel,是经由过程过程间的组件技巧。是以,每次读写Excel中的单位格时,都要进行过程间的切换。当数据量大,若是一个单位格一个单位格的读取,首要的时候都花费在过程切换中。是以读取多个单位格,将可有效的进步法度的运行效力。
对多个单位格的读写操纵可以经由过程CRange中以下两个成员函数来完成。
VARIANT get_Value2();
void put_Value2(VARIANT& newValue);
此中,输入参数newValue只要输入一个二维数组,即可实现向Excel中一次写入多个单位格的值。
此中,VARIANT中实现二维数据的办法可参考
http://www.cnblogs.com/xianyunhe/archive/2011/09/13/2174703.html
当然,在对CRange类进行操纵之前,要设置CRange类对应的单位格。
6、Excel表格的保存
(1)若是要保存打开的工作簿,应用CWorkbook类的Save函数就可以保存工作簿,原文件将被覆盖。
(2)若是是新创建的工作簿,或者是要另存为,可应用CWorkbook类的SaveAs函数。
SaveAs的参数斗劲多。此中,第1个参数是设置要保存文件的路径;第2个参数是设置文件的格局,可在MSDN中查看列举类型XlFileFormat来懂得Excel的文件格局。经过测试,在本文所用的测试景象中,Excel2003的文件格局是xlExcel8,Excel2007的文件格局是xlExcel4。
7、获取当前Excel的版本
可以经由过程CApplication的get_Version函数来获得Excel的版本,此中,Excel2007的主版本号是12,Excel2003的主版本号是11。
8、示例源代码
首要代码如下:
View Code
m_ListCtrl.SetExtendedStyle(LVS_REPORT | LVS_EX_FULLROWSELECT); CApplication ExcelApp; CWorkbooks books; CWorkbook book; CWorksheets sheets; CWorksheet sheet; CRange range; LPDISPATCH lpDisp = NULL; //创建Excel 办事器(启动Excel) if(!ExcelApp.CreateDispatch(_T("Excel.Application"),NULL)) { AfxMessageBox(_T("启动Excel办事器失败!")); return -1; } CString strExcelVersion = ExcelApp.get_Version(); int iStart = 0; strExcelVersion = strExcelVersion.Tokenize(_T("."), iStart); if (_T("11") == strExcelVersion) { AfxMessageBox(_T("当前Excel的版本是2003。")); } else if (_T("12") == strExcelVersion) { AfxMessageBox(_T("当前Excel的版本是2007。")); } else { AfxMessageBox(_T("当前Excel的版本是其他版本。")); } ExcelApp.put_Visible(TRUE); ExcelApp.put_UserControl(FALSE); books.AttachDispatch(ExcelApp.get_Workbooks()); CString strBookPath = _T("C:\\tmp.xls"); try { lpDisp = books.Open(strBookPath, vtMissing, vtMissing, vtMissing, vtMissing, vtMissing, vtMissing, vtMissing, vtMissing, vtMissing, vtMissing, vtMissing, vtMissing, vtMissing, vtMissing); book.AttachDispatch(lpDisp); } catch(...) { lpDisp = books.Add(vtMissing); book.AttachDispatch(lpDisp); } sheets.AttachDispatch(book.get_Sheets()); CString strSheetName = _T("NewSheet"); try { lpDisp = sheets.get_Item(_variant_t(strSheetName)); sheet.AttachDispatch(lpDisp); } catch(...) { lpDisp = sheets.Add(vtMissing, vtMissing, _variant_t((long)1), vtMissing); sheet.AttachDispatch(lpDisp); sheet.put_Name(strSheetName); } system("pause"); lpDisp = sheet.get_Range(_variant_t("A1"), _variant_t("J10")); range.AttachDispatch(lpDisp); VARTYPE vt = VT_I4; SAFEARRAYBOUND sabWrite[2]; sabWrite[0].cElements = 10; sabWrite[0].lLbound = 0; sabWrite[1].cElements = 10; sabWrite[1].lLbound = 0; COleSafeArray olesaWrite; olesaWrite.Create(vt, sizeof(sabWrite)/sizeof(SAFEARRAYBOUND), sabWrite); long (*pArray)[2] = NULL; olesaWrite.AccessData((void **)&pArray); memset(pArray, 0, sabWrite[0].cElements * sabWrite[1].cElements * sizeof(long)); olesaWrite.UnaccessData(); pArray = NULL; long index[2] = {0, 0}; long lFirstLBound = 0; long lFirstUBound = 0; long lSecondLBound = 0; long lSecondUBound = 0; olesaWrite.GetLBound(1, &lFirstLBound); olesaWrite.GetUBound(1, &lFirstUBound); olesaWrite.GetLBound(2, &lSecondLBound); olesaWrite.GetUBound(2, &lSecondUBound); for (long i = lFirstLBound; i <= lFirstUBound; i++) { index[0] = i; for (long j = lSecondLBound; j <= lSecondUBound; j++) { index[1] = j; long lElement = i * sabWrite[1].cElements + j; olesaWrite.PutElement(index, &lElement); } } VARIANT varWrite = (VARIANT)olesaWrite; range.put_Value2(varWrite); system("pause"); CString strSaveAsName = _T("C:\\new.xlsx"); CString strSuffix = strSaveAsName.Mid(strSaveAsName.ReverseFind(_T("".""))); XlFileFormat NewFileFormat = xlOpenXMLWorkbook; if (0 == strSuffix.CompareNoCase(_T(".xls"))) { NewFileFormat = xlExcel8; } book.SaveAs(_variant_t(strSaveAsName), _variant_t((long)NewFileFormat), vtMissing, vtMissing, vtMissing, vtMissing, 0, vtMissing, vtMissing, vtMissing, vtMissing, vtMissing); system("pause"); VARIANT varRead = range.get_Value2(); COleSafeArray olesaRead(varRead); VARIANT varItem; CString strItem; lFirstLBound = 0; lFirstUBound = 0; lSecondLBound = 0; lSecondUBound = 0; olesaRead.GetLBound(1, &lFirstLBound); olesaRead.GetUBound(1, &lFirstUBound); olesaRead.GetLBound(2, &lSecondLBound); olesaRead.GetUBound(2, &lSecondUBound); memset(index, 0, 2 * sizeof(long)); m_ListCtrl.InsertColumn(0, _T(""), 0, 100); for (long j = lSecondLBound; j<= lSecondUBound; j++) { CString strColName = _T(""); strColName.Format(_T("%d"), j); m_ListCtrl.InsertColumn(j, strColName, 0, 100); } for (long i = lFirstLBound; i <= lFirstUBound; i++) { CString strRowName = _T(""); strRowName.Format(_T("%d"), i); m_ListCtrl.InsertItem(i-1, strRowName); index[0] = i; for (long j = lSecondLBound; j <= lSecondUBound; j++) { index[1] = j; olesaRead.GetElement(index, &varItem); switch (varItem.vt) { case VT_R8: { strItem.Format(_T("%d"), (int)varItem.dblVal); } case VT_BSTR: { strItem = varItem.bstrVal; } case VT_I4: { strItem.Format(_T("%ld"), (int)varItem.lVal); } default: { } } m_ListCtrl.SetItemText(i-1, j, strItem); } } sheet.ReleaseDispatch(); sheets.ReleaseDispatch(); book.ReleaseDispatch(); books.ReleaseDispatch(); ExcelApp.Quit(); ExcelApp.ReleaseDispatch();