65.9K
CodeProject 正在变化。 阅读更多。
Home

BasicExcel - 用于读写 Microsoft Excel 的类

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.94/5 (189投票s)

2006年4月20日

10分钟阅读

viewsIcon

5686067

downloadIcon

66178

用于读写 Microsoft Excel 的类

引言

早在 4 年多前,即 2001 年 12 月,我发布了 CSpreadSheet,该类能够读写 Microsoft Excel。那个类相对比较受欢迎。然而,它存在一些不足之处,例如需要标题行,无法将单元格读取为数字等。所有这些都是由于它依赖于 ODBC 驱动程序。对 ODBC 驱动程序的依赖也意味着该类无法在 Unix/Linux 环境中使用。

因此,大约在 3 年前,我想用一个能够以原生 Excel 格式读写文件的类来替换 CSpreadSheet。这将允许该类在 Windows 以外的环境中使用。它还将使用户能够读写电子表格中的数字和字符串。然而,这不是一项容易的任务,因为 Excel 文件保存为复合文件。因此,我必须先编写一个读写复合文件的类,我已经完成了。 (当我有时间好好记录这个类并为其撰写文章时,它将稍后发布。同时,该类实际上已包含在此软件包中。)在我编写完读写复合文件的类之后,我开始使用从 OpenOffice 项目获得的 Excel 文件格式信息来编写读写 Excel 文件的类。可惜,工作承诺阻止了我完成这个项目。

上周,我的工作承诺有所缓解,我突然想起了 3 年前我正在编写的 Excel 类。我想完成这个项目,但我认为 3 年后,肯定有人已经做过类似的项目了。于是我在 SourgeForge 上快速搜索了一下。令我沮丧的是,没有任何此类项目!因此,我决定继续这个项目。我快速查看了我未完成的代码以便熟悉,然后开始编写。现在它终于完成了,我向您介绍 BasicExcel

在我们深入了解 BasicExcel 之前,让我们看看它的局限性。它被称为 BasicExcel 是有原因的。

  1. 它不支持格式设置
  2. 它不支持公式
  3. 它不支持图表
  4. 它不支持 Unicode UTF-32
  5. 它不支持...

实际上,它不支持 Excel 的许多花哨功能。它旨在成为一个基本的类,用于读写电子表格中的简单内容,例如数字和字符串。所以也许最好列出它支持的功能。

  1. 读写数字(整数、实数)和字符串(ANSI、UTF16)
  2. 添加工作表
  3. 重命名工作表
  4. 删除工作表
  5. 获取工作表名称

就这些了。如您所见,BasicExcel 确实非常基础,但即使在其当前形式下,它也比 CSpreadSheet 强大得多。然而,尽管它的功能基础,但编程它并不基础。读写复合文件,以及以原生 Excel 格式读写都需要大量的代码。因此,BasicExcel 的总行数超过 6000 行。

使用代码

我应该列出您需要了解的三个类中的函数,以便使用 BasicExcel。之后将提供示例代码。

class BasicExcel

void New(int sheets=3) 创建一个新的 Excel 工作簿,其中包含指定数量的工作表(最少 1 个)。
bool Load(const char* filename) 从文件加载 Excel 工作簿。
bool Save() 将当前 Excel 工作簿保存到已打开的文件。
bool SaveAs(const char* filename) 将当前 Excel 工作簿另存为文件。
size_t GetTotalWorkSheets() 当前 Excel 工作簿中 Excel 工作表的总数。
BasicExcelWorksheet* GetWorksheet(size_t sheetIndex) 获取指定索引处的 Excel 工作表的指针。索引从 0 开始。如果索引无效,则返回 0
BasicExcelWorksheet* GetWorksheet(const char* name) 获取具有给定 ANSI 名称的 Excel 工作表的指针。如果不存在具有给定名称的 Excel 工作表,则返回 0
BasicExcelWorksheet* GetWorksheet(const wchar_t* name) 获取具有给定 Unicode 名称的 Excel 工作表的指针。如果不存在具有给定名称的 Excel 工作表,则返回 0
BasicExcelWorksheet* AddWorksheet(int sheetIndex=-1) 在指定索引处添加一个新的 Excel 工作表。给工作表起的名称是 SheetX,其中 X 是一个从 1 开始的数字。索引从 0 开始。如果 sheetIndex == -1,则工作表将添加到最后一个位置。成功则返回工作表指针,否则返回 0
BasicExcelWorksheet* AddWorksheet(const char* name, int sheetIndex=-1) 在指定索引处添加一个具有给定 ANSI 名称的新 Excel 工作表。索引从 0 开始。如果 sheetIndex == -1,则工作表将添加到最后一个位置。成功则返回工作表指针,否则返回 0。
BasicExcelWorksheet* AddWorksheet(const wchar_t* name, int sheetIndex=-1) 在指定索引处添加一个具有给定 Unicode 名称的新 Excel 工作表。索引从 0 开始。如果 sheetIndex == -1,则工作表将添加到最后一个位置。成功则返回工作表指针,否则返回 0。
bool DeleteWorksheet(size_t sheetIndex) 删除指定索引处的 Excel 工作表。索引从 0 开始。成功则返回 true,否则返回 false
bool DeleteWorksheet(const char* name) 删除具有给定 ANSI 名称的 Excel 工作表。成功则返回 true,否则返回 false
bool DeleteWorksheet(const wchar_t* name) 删除具有给定 Unicode 名称的 Excel 工作表。成功则返回 true,否则返回 false
char* GetAnsiSheetName(size_t sheetIndex) 获取指定索引处的工作表名称。索引从 0 开始。如果名称是 Unicode 格式,则返回 0
wchar_t* GetUnicodeSheetName(size_t sheetIndex) 获取指定索引处的工作表名称。索引从 0 开始。如果名称是 ANSI 格式,则返回 0
bool GetSheetName(size_t sheetIndex, char* name) 获取指定索引处的工作表名称。索引从 0 开始。如果名称是 Unicode 格式,则返回 false
bool GetSheetName(size_t sheetIndex, wchar_t* name) 获取指定索引处的工作表名称。索引从 0 开始。如果名称是 ANSI 格式,则返回 false
bool RenameWorksheet(size_t sheetIndex, const char* to) 将指定索引处的 Excel 工作表重命名为给定的 ANSI 名称。索引从 0 开始。成功则返回 true,否则返回 false
bool RenameWorksheet(size_t sheetIndex, const wchar_t* to) 将指定索引处的 Excel 工作表重命名为给定的 Unicode 名称。索引从 0 开始。成功则返回 true,否则返回 false
bool RenameWorksheet(const char* from, const char* to) 将具有给定 ANSI 名称的 Excel 工作表重命名为另一个 ANSI 名称。成功则返回 true,否则返回 false
bool RenameWorksheet(const wchar_t* from, const wchar_t* to) 将具有给定 Unicode 名称的 Excel 工作表重命名为另一个 Unicode 名称。成功则返回 true,否则返回 false

class BasicExcelWorksheet

char* GetAnsiSheetName() 获取当前工作表的名称。如果名称是 Unicode 格式,则返回 0
wchar_t* GetUnicodeSheetName() 获取当前工作表的名称。如果名称是 ANSI 格式,则返回 0
bool GetSheetName(char* name) 获取当前工作表的名称。如果名称是 Unicode 格式,则返回 false
bool GetSheetName(wchar_t* name) 获取当前工作表的名称。如果名称是 ANSI 格式,则返回 false
bool Rename(const char* to) 将当前 Excel 工作表重命名为另一个 ANSI 名称。成功则返回 true,否则返回 false
bool Rename(const wchar_t* to) 将当前 Excel 工作表重命名为另一个 Unicode 名称。成功则返回 true,否则返回 false
void Print(ostream& os, char delimiter=',', char textQualifier='\0') 将整个工作表打印到输出流,使用定义的定界符分隔每个列,并使用定义的 textQualifier 包围文本。如果不希望使用任何文本限定符,则省略 textQualifier 参数。
size_t GetTotalRows() 当前 Excel 工作表中的总行数。
size_t GetTotalCols() 当前 Excel 工作表中的总列数。
BasicExcelCell* Cell(size_t row, size_t col) 返回一个 Excel 单元格的指针。row 和 col 从 0 开始。如果 row 超过 65535 或 col 超过 255,则返回 0
bool EraseCell(size_t row, size_t col) 擦除单元格内容。row 和 col 从 0 开始。成功则返回 true,如果 row 或 col 超过范围,则返回 false

class BasicExcelCell

int Type() const 获取当前 Excel 单元格中存储的值的类型。返回以下枚举之一:UNDEFINEDINTDOUBLESTRINGWSTRING
bool Get(int& val) const 获取一个 integer 值。如果单元格不包含 integer,则返回 false
bool Get(double& val) const 获取一个 double 值。如果单元格不包含 double,则返回 false
bool Get(char* str) const 获取一个 ANSI 字符串。如果单元格不包含 ANSI string,则返回 false
bool Get(wchar_t* str) const 获取一个 Unicode 字符串。如果单元格不包含 Unicode string,则返回 false
size_t GetStringLength() 返回 ANSI 或 Unicode 字符串的长度(不包括 null 字符)。
int GetInteger() const 获取一个 integer 值。如果单元格不包含 integer,则返回 0
double GetDouble() const 获取一个 double 值。如果单元格不包含 double,则返回 0.<code>0
const char* GetString() const 获取一个 ANSI string。如果单元格不包含 ANSI string,则返回 0
const wchar_t* GetWString() const 获取一个 Unicode string。如果单元格不包含 Unicode string,则返回 0
ostream& operator<<(ostream& os, const BasicExcelCell& cell) 将单元格打印到输出 stream。如果单元格未定义,则打印一个 null 字符。
void Set(int val) 将当前 Excel 单元格的内容设置为 integer
void Set(double val) 将当前 Excel 单元格的内容设置为 double
void Set(const char* str) 将当前 Excel 单元格的内容设置为 ANSI string
void Set(const wchar_t* str) 将当前 Excel 单元格的内容设置为 Unicode string。
void SetInteger(int val) 将当前 Excel 单元格的内容设置为 integer
void SetDouble(double val) 将当前 Excel 单元格的内容设置为 double
void SetString(const char* str) 将当前 Excel 单元格的内容设置为 ANSI string
void SetWString(const wchar_t* str) 将当前 Excel 单元格的内容设置为 Unicode string
void EraseContents() 擦除当前 Excel 单元格的内容。将类型设置为 UNDEFINED

#include "BasicExcel.hpp"
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("%10d", c+1);
    cout << endl;

    for (size_t r=0; r<maxRows; ++r)
    {
      printf("%10d", 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("%10d", cell->GetInteger());
            break;

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

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

          case BasicExcelCell::WSTRING:
            wprintf(L"%10s", 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("%10d", c+1);
        cout << endl;
      }

      for (size_t r=0; r<maxRows; ++r)
      {
        printf("%10d", 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;
  }
  return 0;
}

参考文献

历史

  • 2006 年 4 月 20 日:发布版本 1。
  • 2006 年 4 月 22 日:发布版本 1.1。修复了复合文件无法写入超过 65535 字节文件的问题。修复了读写包含大量字符串的 Excel 文件的问题。
  • 2006 年 4 月 30 日:发布版本 1.2。添加了 operator<< 以将 BasicExcelCell 传递给输出流。向 BasicExcelWorksheet 添加了 Print() 以将工作表打印到输出流。将 BasicExcelWorksheet 函数 RenameWorkSheet() 重命名为 Rename()。将 BasicExcelCell Get 函数更改为 const 函数。
  • 2006 年 5 月 10 日:发布版本 1.3。修复了读取包含亚洲字符的 Excel 文件的问题。
  • 2006 年 5 月 13 日:发布版本 1.4。修复了读写包含大量字符串的 Excel 文件的问题。
  • 2006 年 5 月 15 日:发布版本 1.5。删除了 ExtSST 的代码,因为它导致了读写包含大量字符串的 Excel 文件的问题。
  • 2006 年 5 月 16 日:发布版本 1.6。优化了读写代码。
  • 2006 年 5 月 22 日:发布版本 1.7。修复了用于消除一些警告的代码。修复了 BasicExcelWorksheet::Cell 的错误。修复了 BasicExcel::UpdateWorksheets() 的错误。
  • 2006 年 5 月 23 日:发布版本 1.8。修复了读取包含大量 Unicode 字符串的 Excel 文件的问题。修复了用于消除一些警告的代码。修复了 BoolErr 中的变量 code_ 重复问题。对 BasicExcelCell:Set 函数进行了少量修改。
  • 2006 年 5 月 24 日:发布版本 1.9。将 Style 中的 name_SmallString 更改为 LargeString。修复了 BasicExcelCell::GetString BasicExcelCell::GetWString 中的错误。对 BasicExcel BasicExcelWorksheet 中检查 unicode 的函数进行了少量修改。对 SmallString::Read 进行了少量修改。
  • 2006 年 5 月 30 日:发布版本 1.10。修复了读取包含大量字符串的 Excel 文件的问题。消除了内存泄漏。为 VC6 用户添加了 BasicExcelVC6.hppBasicExcelVC6.cpp
  • 2006 年 6 月 2 日:发布版本 1.11。修复了读写包含大量 Unicode 和 ANSI 字符串的 Excel 文件的问题。
  • 2006 年 6 月 6 日:发布版本 1.12。修复了读写包含大量 Unicode 和 ANSI 字符串的 Excel 文件的问题。
  • 2006 年 8 月 1 日:发布版本 1.13。更改了 BasicExcelCell::Get() ,使其在必要时将存储的 double 作为 integer 获取,反之亦然。更改了 BasicExcelCell::Get() ,使其不会在字符串为空时产生任何错误。更改了 BasicExcelCell::SetString() BasicExcelCell::SetWString() ,使其不会保存空字符串。
  • 2006 年 8 月 6 日:发布版本 1.14。修复了读取包含 null 字符串的 Excel 文件的问题。

最终注释

如果您在任何程序中使用此类,无论是商业、共享软件、免费软件、开源软件等,我都非常感谢您能给我发一封电子邮件让我知道。得知我的类对某人有用,我会很高兴。

© . All rights reserved.