C++操作Excel学习笔记

 

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的库。

http://www.libxl.com/

按照他的说明,他可以不依赖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应用法度。

WorkbooksCWorkbooksWorkbooks.h工作簿的容器,里面包含了Excel应用法度打开的所有工作簿。
_WorkbookCWorkbook

Workbook.h

单个工作簿。
WorksheetsCWorksheetsWorksheets.h

单个工作簿中的Sheet表格的容器,包含该工作簿中的所有Sheet。

_WorksheetCWorksheetWorksheet.h单个Sheet表格。
RangeCRangeRange.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、示例源代码 

首要代码如下: 

C++操作Excel学习笔记(二)C++操作Excel学习笔记(二)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();
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值