C++操作excel

来源:互联网 发布:mac os x10.9 iso下载 编辑:程序博客网 时间:2024/06/05 16:01

http://blog.sina.com.cn/s/blog_6163bdeb0102dxcy.html

最近要做MFC操作Excel,发现有篇博文介绍VS2008操作Excel的各种方法,不错,收藏了。

原文:http://blog.csdn.net/davidhsing/article/details/4231592

 

这些天做个软件,需要读取 Excel 并导入到数据库中,所以研究了一下在 VC 下操作 Excel 的方法,这里做个总结,以作备忘。

 

一、最常用的 OLE 自动化方式

这个方式应该说是功能最全的方式,可能也是应用的最多的方式。由于这种方式采用的是隐藏启动 Office Excel 的方式,所以几乎是全能,任何功能都可以完成。不过缺点也是比较明显的:

1、采用 OLE 方式,需要用户计算机上安装有 Office Excel,否则就失败;

2、由于是隐藏启动 Office Excel,而 Mcirosoft 的一贯作风就是功能强大、体积巨大无比,所以这个速度是个很大的瓶颈,如果是批量读取的话,那...

3、它的基本方法是使用导出 .h 进行 OLE 操作,但是网上关于 Excel OLE 的完善的参考资料并不是很多,大多也是抄来抄去,所以要想很好的使用它们,恐怕还要好好的探索一下。

不过总之,这种方式在文件数目不多,对功能要求大过速度的话,这是首选。

代码可参考:http://blog.csdn.net/hyz_9257/archive/2008/12/27/3621309.aspx

(一个更好的参考代码见http://www.cppblog.com/greatws/archive/2008/09/21/62423.html

 

二、ADO/ODBC 的方式

这种方式需要确保 ODBC 中已安装有 Excel 表格文件的驱动 "MICROSOFT EXCEL DRIVER (*.XLS)",所以同样依赖于 Office Excel 是否安装。

常规例子:http://www.vckbase.com/document/viewdoc/?id=421

这里不得不提的是:新加坡人 Yap Chun Wei 在 CodeProject 上 2001 年发布的 CSpreadSheet,非常流行的一个 Excel 操作类。地址是:http://www.codeproject.com/KB/database/cspreadsheet.aspx

具体我在最后第六点来介绍和对比。

 

 

三、Sourceforge 上的开源 Excel 库

搜索了一下,有几个似乎人气比较高,像 libXLS,XLSlib 等,下载下来看了一下说明,好像更多是作为 php 的插件来使用的,对 C++ 似乎没多大帮助。

 

 

四、第三方收费 Excel 操作库

第三方收费的,找到一个 LibXL (注意不是 LibXLS),他的官方网站是 http://www.libxl.com/,首先尝试找了一下 cr,可惜没有,由于工期紧,我甚至都考虑要付费购买一份授权,$199 啊!在决定之前,还是先好好测试一下他的功能是否达到我的要求,于是编译了他的几个 Demo,居然... 他自己的 Edit Demo 居然连自己 Generate Demo 程序生成的 Excel 都打不开,俺机器可是安装的有 Office Excel 的呀!实在是,寒

 

 

五、一些不需要依赖 Office Excel 的方式

网上还有一些说的不依赖 Office Excel 的方式,比如 http://www.vckbase.com/document/viewdoc/?id=815,但,说实话,这里的只是理论可行,离实际运用还有很远的路需要走。

 

 

六、重点推荐的

当当当当,BasicExcel !

他的作者和 CSpreadSheet 的作者是同一个,不过是因为他考虑到以前 CSpreadSheet 的一些局限性和想实现兼容性,重新写的。地址是:http://www.codeproject.com/KB/office/BasicExcel.aspx

 

(这个貌似很不错,速度快,操作也还方便,就是不太兼容Unicode,后面原文作者提到,他修改了原代码,可以消除警告,兼容Unicode。demo运行结果如下

image 

demo的代码在最后给出,以示参考 )

 

对比 CSpreadSheet,他的优点在于:

1、CSpreadSheet 采用 CStringArray 赋值和取值,所以对数字等都处理不是很合适;

2、CSpreadSheet  采用 ODBC 方式,ODBC 是拿来当数据库读取和赋值的,所以,必须依赖于 Office 是否安装;ODBC 的速度也是一大瓶颈;而 BasicExcel 直接解析 Excel 格式,从文件下手,速度有相当提高,并且不再依赖于 Office 是否安装;

3、CSpreadSheet 不支持对 Sheet 的操作,而 BasicExcel 可以;

4、CSpreadSheet 需要被读取的 Excel 每一列需要一个列头,比如“姓名”、“年龄”等,不是 Excel 自身的 A、B、AA 等,没办法,要作为 ODBC 数据源使用,需要相当于数据库表的列名一样,这是不可避免的;

...

 

众多优点,是 CSpreadSheet 不能比的,所以作者在 2006 年新发布了这个类。

我的起点,便在这里。

 

由于作者在发布这一款操作类的时候,考虑的更多是跨平台性,Windows/(*)nix,所以采用的都是标准 C++ 函数,如 wcstombs、wcscpy 等,而这些在 Visual Studio 在被认为是不安全的,Microsoft 认为存在缓冲区溢出的隐患,所以 Microsoft 建立了一系列自己的函数,加了个 _s,如 wcstombs_s 等,另外由于不同的平台对 int short 等的精度不一样,所以还会有“初始化截断常量”这样怪异的警告。总共编译下来居然有 200 多个 warnning,要崩溃了!于是一个一个修改,终于到最后消灭了所有的 warnning。

另外一个我做的很大的调整就是完善对 Unicode 的支持。原类中作者似乎并没有考虑到这些,或者说是考虑的不全(谁叫人家新加坡的官方语言是英语呢?),所以只要打开或者保存的文件中含有中文,必定失败,被这个问题折腾了好久,一点点修改、调试。增加了很多 wchar_t* 形式的重载函数。

对这些类的收集、分析和 Basci Excel 的修改和调试,整整用了我 5 天时间!不过好在,到昨天凌晨,终于大功告成了,可以完美的支持 Unicode 了,嘿嘿

 

 

BasicExcel的demo示例:

#include "BasicExcel.hpp"
#include <stdlib.h>
using namespace YExcel;

int main(int argc, char* argv[])
{
    BasicExcel e;

    // Load a workbook with one sheet, display its contents and save into another file.
    e.Load("example1.xls");   
    BasicExcelWorksheet* sheet1 = e.GetWorksheet("Sheet1");
    if (sheet1)
    {
        size_t maxRows = sheet1->GetTotalRows();
        size_t maxCols = sheet1->GetTotalCols();
        cout << "Dimension of " << sheet1->GetAnsiSheetName() << " (" << maxRows << ", " << maxCols << ")" << endl;

        printf("          ");
        for (size_t c=0; c<maxCols; ++c) printf("d", c+1);
        cout << endl;

        for (size_t r=0; r<maxRows; ++r)
        {
            printf("d", r+1);
            for (size_t c=0; c<maxCols; ++c)
            {
                BasicExcelCell* cell = sheet1->Cell(r,c);
                switch (cell->Type())
                {
                    case BasicExcelCell::UNDEFINED:
                        printf("          ");
                        break;

                    case BasicExcelCell::INT:
                        printf("d", cell->GetInteger());
                        break;

                    case BasicExcelCell::DOUBLE:
                        printf(".6lf", cell->GetDouble());
                        break;

                    case BasicExcelCell::STRING:
                        printf("s", cell->GetString());
                        break;

                    case BasicExcelCell::WSTRING:
                        wprintf(L"s", cell->GetWString());
                        break;
                }
            }
            cout << endl;
        }
    }
    cout << endl;
    e.SaveAs("example2.xls");

    // Create a new workbook with 2 worksheets and write some contents.
    e.New(2);
    e.RenameWorksheet("Sheet1", "Test1");
    BasicExcelWorksheet* sheet = e.GetWorksheet("Test1");
    BasicExcelCell* cell;
    if (sheet)
    {
        for (size_t c=0; c<4; ++c)
        {
            cell = sheet->Cell(0,c);
            cell->Set((int)c);
        }

        cell = sheet->Cell(1,3);
        cell->SetDouble(3.141592654);

        sheet->Cell(1,4)->SetString("Test str1");
        sheet->Cell(2,0)->SetString("Test str2");
        sheet->Cell(2,5)->SetString("Test str1");

        sheet->Cell(4,0)->SetDouble(1.1);
        sheet->Cell(4,1)->SetDouble(2.2);
        sheet->Cell(4,2)->SetDouble(3.3);
        sheet->Cell(4,3)->SetDouble(4.4);
        sheet->Cell(4,4)->SetDouble(5.5);

        sheet->Cell(4,4)->EraseContents();
    }

    sheet = e.AddWorksheet("Test2", 1);
    sheet = e.GetWorksheet(1);
    if (sheet)
    {
        sheet->Cell(1,1)->SetDouble(1.1);
        sheet->Cell(2,2)->SetDouble(2.2);
        sheet->Cell(3,3)->SetDouble(3.3);
        sheet->Cell(4,4)->SetDouble(4.4);
        sheet->Cell(70,2)->SetDouble(5.5);
    }
    e.SaveAs("example3.xls");

    // Load the newly created sheet and display its contents
    e.Load("example3.xls");

    size_t maxSheets = e.GetTotalWorkSheets();
    cout << "Total number of worksheets: " << e.GetTotalWorkSheets() << endl;
    for (size_t i=0; i<maxSheets; ++i)
    {
        BasicExcelWorksheet* sheet = e.GetWorksheet(i);
        if (sheet)
        {
            size_t maxRows = sheet->GetTotalRows();
            size_t maxCols = sheet->GetTotalCols();
            cout << "Dimension of " << sheet->GetAnsiSheetName() << " (" << maxRows << ", " << maxCols << ")" << endl;

            if (maxRows>0)
            {
                printf("          ");
                for (size_t c=0; c<maxCols; ++c) printf("d", c+1);
                cout << endl;
            }

            for (size_t r=0; r<maxRows; ++r)
            {
                printf("d", r+1);
                for (size_t c=0; c<maxCols; ++c)
                {
                    cout << setw(10) << *(sheet->Cell(r,c));    // Another way of printing a cell content.               
                }
                cout << endl;
            }
            if (i==0)
            {
                ofstream f("example4.csv");
                sheet->Print(f, ',', '\"');    // Save the first sheet as a CSV file.
                f.close();
            }
        }
        cout << endl;
    }

    system("pause");
    return 0;
}