ExcelFormat 库






4.93/5 (117投票s)
ExcelFormat 允许使用 C++ 读取、写入和编辑 XLS (BIFF8 格式) 文件。
引言
C++ 项目 BasicExcel 已经存在多年。它对于以 .xls 格式读取和写入 Excel 工作表非常有用。然而,它只包含非常基本的功能。缺少诸如使用字体进行文本格式化等功能,并且尚不支持显示格式和公式。本文涵盖了这些附加功能。新的派生项目 ExcelFormat
基于旧的 BasicExcel
代码,并且只添加了我自己需要的功能。如果您想了解我在哪里使用了新库,您可以查看 服务管理器 和 卸载管理器 的导出功能。
XLS 格式
此库处理 BIFF8 XLS 文件格式的 Excel 文件。有关格式及其所有内部结构的详细文档,有两份文档可用
- OpenOffice 提供 OpenOffice.org 的 Microsoft Excel 文件格式文档。
- 几年后,微软发布了 Microsoft Office 二进制文件格式。
兼容性
您可以在 MS Windows 上使用 VC++ 6.0 SP3 或更高版本的 MSVC 编译器来使用该代码。还支持在 MacOSX 或 Linux 上使用 GCC,但存在一些限制。可能它甚至可以在 Sun Solaris 等其他环境中运行,但尚未测试。以下是各种环境及其状态的表格
小型 XLS 文件 | 大型 XLS 文件 | |
---|---|---|
WIN32,使用 Windows API IStorage | 好的 | 好的 |
WIN64,使用 Windows API IStorage | 好的 | 好的 |
MacOS X / Linux 32 位,使用 BasicExcel CompoundFile 实现 | 好的 | 问题 |
MacOS X / Linux 64 位,使用 BasicExcel CompoundFile 实现 | 好的 | 问题 |
上述问题是由于底层 BasicExcel
库的 CompoundFile
实现不完整。我已经修复了 BasicExcel
代码中关于 RKValue
格式的数字和 64 位兼容性的一些部分,但当读取或写入使用 XBAT 或 SBAT 条目的大型 XLS 文件时,仍然会出现问题。
Using the Code
要使用新的格式化功能,首先创建一个 XLSFormatManager
对象,如 example1()
函数中所示,并将其附加到现有的 BasicExcel
对象
void example1(const char* path)
{
BasicExcel xls;
// create sheet 1 and get the associated BasicExcelWorksheet pointer
xls.New(1);
BasicExcelWorksheet* sheet = xls.GetWorksheet(0);
XLSFormatManager fmt_mgr(xls);
您可以在源代码文件 Examples.cpp 中找到本文的所有示例。
要定义自定义字体,请创建一个 ExcelFont
对象并设置任何所需的属性,例如,粗体字的字体粗细
ExcelFont font_bold;
font_bold._weight = FW_BOLD; // 700=bold, 400=normal
Excel 单元格的格式可以通过 CellFormat
对象定义,该对象包含所选字体和更多属性
CellFormat fmt_bold(fmt_mgr);
fmt_bold.set_font(font_bold);
准备好 CellFormat
后,您可以通过调用 SetFormat()
来选择 Excel 单元格的字体和显示设置
// Create a table containing a header row in bold and four rows below.
int col, row = 0;
for(col=0; col<10; ++col) {
BasicExcelCell* cell = sheet->Cell(row, col);
cell->Set("TITLE");
cell->SetFormat(fmt_bold);
}
while(++row < 4) {
for(int col=0; col<10; ++col)
sheet->Cell(row, col)->Set("text");
}
++row;
文本颜色通过在 ExcelFont
中设置颜色索引来指定,例如
ExcelFont font_red_bold;
font_red_bold._weight = FW_BOLD;
font_red_bold._color_index = EGA_RED;
CellFormat fmt_red_bold(fmt_mgr, font_red_bold);
fmt_red_bold.set_color1(COLOR1_PAT_SOLID); // solid background
fmt_red_bold.set_color2(MAKE_COLOR2(EGA_BLUE,0)); // blue background
CellFormat fmt_green(fmt_mgr, ExcelFont().set_color_index(EGA_GREEN));
for(col=0; col<10; ++col) {
BasicExcelCell* cell = sheet->Cell(row, col);
cell->Set("xxx");
cell->SetFormat(fmt_red_bold);
cell = sheet->Cell(row, ++col);
cell->Set("yyy");
cell->SetFormat(fmt_green);
}
ExcelFormat.h 包含用于在枚举 EXCEL_COLORS
中定义基本调色板颜色的常量,您可以在调用 ExcelFont()::set_color_index()
时使用它们。宏 MAKE_COLOR2
接受两个颜色索引来指定图案线和图案背景颜色。作为调用 CellFormat::set_color1()
和 CellFormat::set_color2()
的快捷方式,您还可以使用 CellFormat::set_background()
来定义具有纯背景颜色或着色图案的单元格。
在内存中创建和格式化 Excel 单元格后,您所要做的就是将新的 Excel 工作表保存为文件
xls.SaveAs(path);
}
这足以快速概述如何使用新的 ExcelFormat
对象。在源代码文件 ExcelFormat.cpp 中,您可以找到更多示例。
ExcelFont
有九个字体属性可用
struct ExcelFont
{
ExcelFont()
...
wstring _name;
short _height;
short _weight;
short _options;
short _color_index;
short _escapement_type;
char _underline_type;
char _family;
char _character_set;
...
};
有关详细信息,请查看 XLS 文件格式文档。
单元格格式
除了所选的 ExcelFont
和显示格式之外,还提供以下 CellFormat
属性
struct CellFormat
{
CellFormat(XLSFormatManager& mgr)
...
char _alignment;
char _rotation;
char _text_props;
int _borderlines;
int _color1;
short _color2;
...
};
在 example2()
中,您可以看到如何更改字体和字号
ExcelFont font_header;
font_header.set_weight(FW_BOLD);
font_header.set_underline_type(EXCEL_UNDERLINE_SINGLE);
font_header.set_font_name(L"Times New Roman"); // font face "Times New Roman"
font_header.set_color_index(EGA_BLUE);
font_header._options = EXCEL_FONT_STRUCK_OUT;
CellFormat fmt_header(fmt_mgr, font_header);
fmt_header.set_rotation(30); // rotate the header cell text 30° to the left
int row = 0;
for(int col=0; col<10; ++col) {
BasicExcelCell* cell = sheet->Cell(row, col);
cell->Set("TITLE");
cell->SetFormat(fmt_header);
}
example3()
函数演示了如何使用 CellFormat::set_format_string()
和 BasicExcelCell::SetFormat()
来定义文本、数字和日期格式 string
。格式 string
有一些预定义的常量
#define XLS_FORMAT_GENERAL L"General"
#define XLS_FORMAT_TEXT L"@"
#define XLS_FORMAT_INTEGER L"0"
#define XLS_FORMAT_DECIMAL L"0.00"
#define XLS_FORMAT_PERCENT L"0%"
#define XLS_FORMAT_DATE L"M/D/YY"
#define XLS_FORMAT_TIME L"h:mm:ss"
#define XLS_FORMAT_DATETIME L"M/D/YY h:mm"
但是,您可以使用任何有效的 Excel 格式 string
来定义自定义显示格式。
example4()
展示了如何在一个工作表中使用多种不同的字体和颜色
公式
现在,在读取和写入 Excel 工作表时,公式将得到保留。您甚至可以通过调用 BasicExcelCell::SetFormula()
将公式存储到 Excel 单元格中。但是,目前,您要么必须从现有单元格中复制一个 Worksheet::CellTable::RowBlock::CellBlock::Formula
对象,要么必须自行定义,这相当棘手,因为 Excel 使用包含 RPN 标记的预解析公式。
关注点
对于那些对背景信息感兴趣的人,我想提供一个关于自前身库 BasicExcel 以来发生的变化的描述。
条件编译
存在两种不同的 BasicExcel
实现,一种用于 VC++ 6.0 版本,另一种用于更新的编译器。ExcelFormat
现在通过使用条件编译来处理这些差异,从而合并了这两个代码库。这大部分是通过此代码片段(以及使用新定义的宏进行的修改)在头文件中完成的
#if _MSC_VER<=1200 // VC++ 6.0
#pragma warning(disable: 4786)
#define LONGINT __int64
#define LONGINT_CONST(x) x
#define COMPOUNDFILE
#else // newer Microsoft compilers
#define LONGINT long long
#define LONGINT_CONST(x) x##LL
#define COMPOUNDFILE CompoundFile::
#ifdef _DEBUG
#define _ITERATOR_DEBUG_LEVEL 0 // speedup iterator operations while debugging
#endif
#endif
为了区分使用 MSVC 的 MS Windows 环境和使用 GCC 的其他环境,会测试宏 _MSC_VER 是否存在。这在访问复合二进制文件格式时在 Windows API 和 BasicExcel 的 CompoundFile 实现之间进行切换。
#pragma warning
语句禁用了 VC++ 6.0 的编译器警告,原因是对象文件中存在长的编译器内部名称,这在使用 STL 类进行非平凡操作时会发生。
紧接着这些定义之后,是适用于 VS 2005 及更高版本的这些预处理器语句
#if _MSC_VER>=1400 // VS 2005
#define _CRT_SECURE_NO_WARNINGS //MF
#define _SCL_SECURE_NO_WARNINGS //MF
#endif
它们禁用了 VC++ 安全库警告,因为 BasicExcel
代码尚未为这些新的运行时库添加做好准备。顺便说一下,MF 注释标记了我对旧 BasicExcel
代码的添加和修复。
此外,我通过修复数据类型消除了一些编译器警告。通常,这是从 int
等整型类型到 C 运行时库类型 size_t
的更改。
要在使用 GCC 时切换 32 位和 64 位模式,请使用编译器选项 -m32
或 -m64
,如源代码下载中的 Makefile 示例所示。在 VC++ 环境中,您可以在项目设置中选择目标环境。
新功能
除了上面描述的新 API,我必须在 BasicExcel
中添加代码以实现这些新功能
- 从 XLS 文件读取和写入
Formula
结构 - 定义、读取和写入 XLS 文件中的
FORMAT
结构 - 获取/设置所有各种单元格类型的
BasicExcelCell
对象的 XF 索引值
格式存储结构
Excel 单元格在 BIFF8 文件格式中的格式信息使用所谓的 XF 索引存储。这指的是 XF(“扩展格式”)记录,该记录由以下成员组成
XF {
short fontRecordIndex // FORMAT index
short formatRecordIndex // FONT index
short protectionType
char alignment
char rotation
char textProperties
char usedAttributes
int borderLines
int colour1
short colour2
}
除了少数直接关联的属性(指定 Excel 单元格的对齐、旋转等)之外,还有两个索引值:fontRecordIndex
和 formatRecordIndex
。它们用于定义字体和显示格式描述。从整体来看,这种基于两级索引的格式体系结构能够以较小的文件大小和较低的内存使用率实现单元格格式化,因为 Excel 工作表中通常只使用少数几种不同的字体和显示格式。字体索引指向一个具有以下属性的 FONT
记录
FONT {
short height
short options
short colourIndex
short weight
short escapementType
char underlineType
char family
char characterSet
char unused
SmallString name
}
第三个索引是一个特殊的索引。此格式索引与一个只包含索引本身和显示格式文本表示的记录相关联
FORMAT {
short index
LargeString fmtstring
}
XLSFormatManager
管理这三个格式子结构,使用 C++ 结构 CellFormat
和 ExcelFont
来格式化 Excel 单元格
struct CellFormat -> XF {FORMAT index, FONT index, XF attributes}
struct ExcelFont -> FONT {FONT attributes}
当调用 CellFormat::set_font(const ExcelFont& font)
时,管理器类会搜索已注册的匹配字体描述。如果还没有,则会创建一个新的 FONT
记录存储在 Excel 工作表中。当调用 CellFormat::set_format_string(const wstring& fmt_str)
时,管理器类会搜索已注册的匹配显示格式 string
。如果还没有,则会创建一个新的 FORMAT
记录存储显示格式 string
。当通过调用 BasicExcelCell::SetFormat(const CellFormat& fmt)
将 CellFormat
应用于单元格对象时,也会使用相同的策略:管理器类会搜索已注册的匹配 XF 描述,该描述具有相同的字体和格式索引以及匹配的 XF 属性。如果还没有,则会创建一个新的 XF
记录存储在 Excel 工作表中。最终,这将生成一个 FORMAT
、FONT
和 XF
记录列表,这些记录存储在 Excel 工作簿文件的标题中。每个单元格都通过存储一个相关的 XF 索引进行格式化,该索引确定所有单元格格式属性、字体和显示格式。
有关更多实现细节,请查阅源代码中的 ExcelFormat.h、ExcelFormat.cpp、BasicExcel.hpp 和 BasicExcel.cpp。
内存使用
为了限制内存使用,使用引用计数 SmartPtr
来管理公式存储所需的堆结构。每个单元格只包含一个可选填充的指向 struct Formula
的指针。对于文本或数字单元格,不需要公式信息。因此智能指针保持为空,只包含值 NULL
。
在以下部分中,您可以看到 struct RefCnt
作为 struct Formula
的基础以及模板 struct SmartPtr
的实现,用于保存引用计数的堆对象
// reference counter for SmartPtr managed objects
struct RefCnt
{
// On construction the reference counter
// is initialized with an usage count of 0.
RefCnt()
: _ref_cnt(0)
{
}
int _ref_cnt;
};
// reference counting smart pointer
template<typename T> struct SmartPtr
{
// default constructor
SmartPtr()
: _ptr(NULL)
{
}
// The initialized SmartPtr constructor increments the reference counter
// in struct RefCnt.
SmartPtr(T* p)
: _ptr(p)
{
if (p)
++_ptr->_ref_cnt;
}
// The copy constructor increments the reference counter.
SmartPtr(const SmartPtr& other)
: _ptr(other._ptr)
{
if (_ptr)
++_ptr->_ref_cnt;
}
// The destructor decreases the reference counter and
// frees the managed memory as the counter reaches zero.
~SmartPtr()
{
if (_ptr) {
if (!--_ptr->_ref_cnt)
delete _ptr;
}
}
// The assignment operator increments the reference counter.
SmartPtr& operator=(T* p)
{
if (_ptr) {
if (!--_ptr->_ref_cnt)
delete _ptr;
_ptr = NULL;
}
if (p) {
_ptr = p;
++_ptr->_ref_cnt;
}
return *this;
}
// operator bool() to check for non-empty smart pointers
operator bool() const {return _ptr != NULL;}
// operator!() to check for empty smart pointers
bool operator!() const {return !_ptr;}
// operator->() to access the managed objects
T* operator->() {return _ptr;}
const T* operator->() const {return _ptr;}
// Dereference pointed memory
T& operator*() {return *_ptr;}
const T& operator*() const {return *_ptr;}
private:
T* _ptr;
};
字符串转换
有一些新的 string
转换函数:stringFromSmallString()
、stringFromLargeString()
、wstringFromSmallString()
、wstringFromLargeString()
使用 narrow_string()
/widen_string()
用于将内部 Excel string
结构转换为 STL string
类,反之亦然。您可以使用它们访问 BasicExcel
的内部数据存储。
历史
- 2009年9月20日 -
ExcelFormat
1.0 版(BasicExcel
2.0 版) - 2009年9月28日 - 2.0 版
- 添加新章节“格式化存储结构”
- 2009年10月4日 - 2.1 版
- 更新源代码,添加单元格和字体属性的宏和常量
- 2009年11月7日 - 2.2 版
- 修复了 VS2008 读取带公式字段的工作表时的问题
- 添加了
BasicExcel::Close()
、CellFormat::get
/set_text_props()
和get/set_borderlines()
- 2010年1月12日 - 2.3 版:(由 Ami Castonguay 和 Martin Fuchs)
- 修复了公式数据结构的引用计数
- 支持共享公式
- 支持合并单元格
- 即使单元格为空也保存格式
- 刷新
fstream
而不是关闭后再打开,以防止与病毒扫描程序结合使用时出现竞争条件 - 支持读取 MacOS Numbers.app 导出的 XLS 文件
- 2010年11月15日 - 2.4 版
- 添加第二个
set_borderlines()
重载 - 添加
ExcelFont::set_italic()
、CellFormat::set_wrapping()
- 处理
COLINFO
- 杂项修复
- 添加第二个
- 2011年1月1日 - 2.5 版
- 在加载 XLS 文件时,为意外的高行/列值动态分配内存
Load()
和SaveAs()
的 Unicode 重载- 调整以适应 OpenOffice Calc 写入的
RKValues
- 2011年2月3日 - 3.0 版
- 使用 Windows API 访问复合文档文件
- 减少内存消耗并提高速度
- 64 位可移植性
- 从公式单元格返回当前值字符串