Click here to Skip to main content
Click here to Skip to main content

ExcelFormat Library

By , 3 Feb 2011
 

example1.png

Introduction

There has been the C++ project BasicExcel for quite some years now. It is very useful to read and write Excel sheets in .xls format. However, it contains only very basic functionality. There are missing features like text formatting using fonts, and display formats and formulas are not yet supported. This article covers this additional functionality. The new, derived project ExcelFormat is based on the old BasicExcel code, and just adds what I was missing for my own needs. If you want to see where I am using the new library, you can look at the Export functions of Service Manager and Uninstall Manager.

XLS Format

This library processes Excel files in the BIFF8 XLS file format. For a detailed documentation of the format and all its internals, there are two documents available:

Compatibility

You can use the code on MS Windows with the MSVC compilers of VC++ 6.0 SP3 or later. Using GCC on MacOSX or Linux is also supported, but with some limitations. May be it even works in other environments like Sun Solaris, but this is not yet tested. Here you can see a table of environments with their respective status:

small XLS fileslarger XLS files
WIN32, using Windows API IStorageOKOK
WIN64, using Windows API IStorageOKOK
MacOS X / Linux 32 bit, using BasicExcel CompoundFile implementationOKissues
MacOS X / Linux 64 bit, using BasicExcel CompoundFile implementationOKissues

The issues listed above are because of the incomplete CompoundFile implementation of the underlying BasicExcel library. I already fixed some part of the BasicExcel code regarding numbers in RKValue format and 64 bit compatibility, but there are still issues that show up when reading or writing larger XLS files which use XBAT or SBAT entries.

Using the Code

To use the new formatting functionality, first create an XLSFormatManager object, like in the example1() function, and attach it to an existing BasicExcel object:

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);

You can find all the examples of this article in the source code file Examples.cpp.

To define a custom font, create an ExcelFont object and set any needed properties, for example, the font weight for a bold font:

ExcelFont font_bold;
font_bold._weight = FW_BOLD;	// 700=bold, 400=normal

The format of an Excel cell can be defined by a CellFormat object, which holds the chosen font and some more properties:

CellFormat fmt_bold(fmt_mgr);
fmt_bold.set_font(font_bold);

After you have prepared the CellFormat, you can choose the font and display settings of Excel cells by calling SetFormat():

// 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;

Text color is specified by setting color indices in ExcelFont, for example:

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 contains constants to define basic palette colors in the enumeration EXCEL_COLORS, you can use in calls to ExcelFont()::set_color_index(). The macro MAKE_COLOR2 accepts two color indices to specify the pattern line and pattern background colors. As a shortcut to calling CellFormat::set_color1() and CellFormat::set_color2(), you can also use CellFormat::set_background() to define cells with solid background colors or colorize patterns.

After creating and formatting the Excel cells in memory, all you have to do is to save the new Excel sheet as a file:

    xls.SaveAs(path);
}

This should be enough for a quick overview on how to use the new ExcelFormat objects. In the source code file ExcelFormat.cpp, you can find some more examples.

ExcelFont

There are nine font properties available:

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;
 
    ...
 
};

For detailed information, please look into the XLS file format documentation.

CellFormat

These CellFormat properties are available in addition to the chosen ExcelFont and the display format:

struct CellFormat
{
    CellFormat(XLSFormatManager& mgr)
 
    ...
 
    char    _alignment;
    char    _rotation;
    char    _text_props;
    int     _borderlines;
    int     _color1;
    short   _color2;
 
    ...
 
};

In example2(), you can see how to change the font face and the font size:

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);
}

The example3() function shows how to use CellFormat::set_format_string() and BasicExcelCell::SetFormat() to define text, number, and date format strings. There are some predefined constants for format strings:

#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"

However, you can use any valid Excel format string to define custom display formats.

example4() shows how to use many different fonts and colors in one sheet:

example4.png

Formulas

Formulas are now preserved when reading and writing an Excel sheet. You can even store a formula into an Excel cell by calling BasicExcelCell::SetFormula(). However, currently, you either have to copy a Worksheet::CellTable::RowBlock::CellBlock::Formula object from an already existing cell, or you have to define it yourself, which is quite tricky because Excel uses a pre-parsed formula containing RPN tokens.

Points of Interest

For those who are interested in a bit of background information, I want to provide a description of what has been changed since the predecessor library BasicExcel.

Conditional Compilation

There are two different BasicExcel implementations, one for VC++ version 6.0, and one for newer compilers. ExcelFormat now merges these two code bases by using conditional compilation to handle the differences. Most of this is accomplished in the header file by this code snippet (and modifications using the newly defined macros):

#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

To distinguish between MS Windows environments using MSVC and others using GCC, the macro _MSC_VER is tested for existence. This switches between using the Windows API to access the compound binary file format and the BasicExcel implementation of CompoundFile.

The #pragma warning statement disables compiler warnings of VC++ 6.0 because of long compiler internal names in object files, which occur for non-trivial usage of STL classes.

Immediately after these defines, there are these preprocessor statements for VS 2005 and above:

#if _MSC_VER>=1400    		// VS 2005
#define _CRT_SECURE_NO_WARNINGS    	//MF
#define _SCL_SECURE_NO_WARNINGS    	//MF
 
#endif

They disable the VC++ security library warnings, because the BasicExcel code is not yet prepared for this new runtime library additions. By the way, the MF comments mark additions and fixes by me to the old BasicExcel code.

Additionally, I eliminated some compiler warnings by fixing data types. Typically, this was a change from integral types like int to the C runtime library type size_t.

To switch between 32 bit and 64 bit mode using GCC, use the compiler option -m32 respective -m64 like in the Makefile example in the source download. In the VC++ environment you can choose the target environment in the project settings.

New Features

In addition to the new API described above, I had to add code in BasicExcel in order to implement these new features:

  • Read and write the Formula structure from/to XLS files
  • Define, read, and write the FORMAT structure from/to XLS files
  • Get/set the XF index value BasicExcelCell objects for all the various cell types

Formatting Storage Structures

The information about the format of an Excel cell in the BIFF8 file format is stored using a so called XF index. This refers to an XF (extended format) record, which consists of the following members:

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
}

Besides a few directly associated attributes specifying alignment, rotation, etc. of the Excel cell, there are two more index values: fontRecordIndex and formatRecordIndex. These are used to define font and display format descriptions. In the whole picture, this two-level index based formatting architecture enables cell formatting with small file sizes and low memory usage, because there are typically only a few different fonts and display formats used in an Excel worksheet. The font index refers to a FONT record with the following attributes:

FONT {
    short       height
    short       options
    short       colourIndex
    short       weight
    short       escapementType
    char        underlineType
    char        family
    char        characterSet
    char        unused
    SmallString name
}

The third index is a special one. This format index is associated with a record containing only the index itself and a text representation of the display format:

FORMAT {
    short       index
    LargeString fmtstring
}

XLSFormatManager manages these three formatting substructures to format Excel cells using the C++ structures CellFormat and ExcelFont:

struct CellFormat  ->  XF {FORMAT index, FONT index, XF attributes}
 
struct ExcelFont   ->  FONT {FONT attributes}

On calling CellFormat::set_font(const ExcelFont& font), the manager class searches for an already registered matching font description. If there is none yet, a new FONT record is created to be stored in the Excel sheet. On calling CellFormat::set_format_string(const wstring& fmt_str), the manager class searches for an already registered matching display format string. If there is none yet, a new FORMAT record stores the display format string. The same strategy is used when applying a CellFormat to a cell object by calling BasicExcelCell::SetFormat(const CellFormat& fmt): the manager class searches for an already registered matching XF description with identical font and format indices as well as matching XF attributes. If there is none yet, a new XF record is created to be stored in the Excel sheet. In the end, this results in a list of FORMAT, FONT, and XF records, which are stored in the header of the Excel workbook file. Each cell is formatted by storing a single related XF index, which determines all the cell formatting attributes, the font, and the display format.

For further implementation details, please have a look into ExcelFormat.h, ExcelFormat.cpp, BasicExcel.hpp and BasicExcel.cpp of the source code.

Memory Usage

To limit memory usage, a reference counting SmartPtr is used to manage the heap structures needed for formula storage. Every cell contains only an optionally filled pointer to the struct Formula. For text or number cells, formula information is not needed. So the smart pointer stays empty, and just contains the value NULL.

In the following section, you can see the implementation of the struct RefCnt as the base for struct Formula and the template struct SmartPtr to hold the reference counted heap objects:

// 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 Conversion

There are some new string conversion functions: stringFromSmallString(), stringFromLargeString(), wstringFromSmallString(), wstringFromLargeString() using narrow_string()/widen_string() are used to convert from internal Excel string structures to STL string classes and vice versa. You can use them to access the internal data storage of BasicExcel.

History

  • 20.09.2009 - Version 1.0 of ExcelFormat (BasicExcel Version 2.0)
  • 28.09.2009 - Version 2.0
    • Added new section Formatting storage structures
  • 04.10.2009 - Version 2.1
    • Updated source code with macros and constants for cell and font properties
  • 07.11.2009 - Version 2.2
    • Fixed VS2008 problem when reading sheets with formula fields
    • Added BasicExcel::Close(), CellFormat::get/set_text_props() and get/set_borderlines()
  • 12.01.2010 - Version 2.3: (by Ami Castonguay and Martin Fuchs)
    • Fixed reference counting of Formula data structs
    • Support for shared formulas
    • Support for merged cells
    • Save formatting even if cell is empty
    • Flush fstream instead of closing it followed by open to prevent races in conjunction with virus scanners
    • Enable reading of XLS files exported by MacOS Numbers.app
  • 15.11.2010 - Version 2.4
    • Add second set_borderlines() overload
    • Add ExcelFont::set_italic(), CellFormat::set_wrapping()
    • Handle COLINFO
    • Miscellaneous fixes
  • 01.01.2011 - Version 2.5
    • Dynamically allocate memory for unexpected high row/column values while loading XLS files
    • Unicode overloads for Load() and SaveAs()
    • Adjust to RKValues written by OpenOffice Calc
  • 03.02.2011 - Version 3.0
    • access compound document files using the Windows API
    • reduced memory consumption and increased speed
    • 64 bit portability
    • return current value string from formula cells

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Martin Fuchs
Software Developer (Senior)
Germany Germany
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Questioncan't load an excel using the ExcelFormat Librarymemberwumo_5183hrs 55mins ago 
Hi, I have used your ExcelFormat Library in a project to read a tilemap config. but when i am trying to load an excel an error occured.
the load file code:
BasicExcel excel;
    wchar_t filename[256] = { 0 };
    MultiByteToWideChar(CP_ACP, 0, lpCmdLine, strlen(lpCmdLine), filename, 256);
 
    if (!excel.Load(filename))
    {
        g_DebugConsole.format("加载Tilemap文件\"%s\"失败", filename);
        g_DebugConsole.checkWait();
        return ;
    }
 
and the error :
"Debug Assertion Failed!...Expression: vector subscript out of range".
 
and i deleted some rows then try again, it loaded successed.
 
any idea why?
 
if you can send me an email and then i can reply you with my excel file and the screenshot of the error i will really
appreciate. my email address is : wumo518@gmail.com
 
Thanks very much!
yours
wumo
GeneralMy vote of 5membergage9712-Jun-13 11:40 
very informative
QuestionThanksmemberBLKNABI28-May-13 23:21 
It's thing that i found. thanks.
 
but..
 
project demo, (execute file in bin.zip) have error.
 
i can't excute that file.
 
"BEBUG..."
 
I can see "Error MessageBox"....
QuestionCrash in Workbook::SharedStringTable::ReadmemberAlexSad13-May-13 5:43 
Hello,
 
First off all, thank you so much for such a great library! I've been using it with a big success.
Unfortunately, there seems to be some bug in reading shared string table. I've got an XLS file that crashes ExcelFormat during load.
 
So, I was just wondering if somebody has had the same problem and if there is a known way to fix it.
 
Thanks.
QuestionHow to use code to change a cell without save and loadmemberjackzhoujun1-May-13 6:57 
Hi,
 
My question is:
how can I treat a .xls as a file stream and write to it directly without user-action save. Like a real-time data logger.
 
Thanks
Questioncell->GetString() issue for strings containing xE9(è)memberChristie Lukose17-Apr-13 2:20 
Help!!!
I have an excel file with French strings in rows
(1) Choisir la difficulté
(2) Défausse d’une carte
 
when loading the excel file
String (1) loads into cell->GetString();
String (2) loads into cell->GetWString();
Confused | :confused:
I want both the strings be loaded as cell->GetWString()
 
Could you please tell me what in excel file format(BIFF) causes this?
and how can I overcome it.
Christie

QuestionBug reading an .XLS file in SmallString::Read callmemberpcunite8-Apr-13 14:42 
Is this library still maintained? I've found a bug in SmallString::Read when reading a certain type of XLS file.
AnswerRe: Bug reading an .XLS file in SmallString::Read callmemberMartin Fuchs8-Apr-13 20:38 
If you post the fix, I am happy to merge it into the code.
 
Regards,
 
Martin
Martin Fuchs
martin-fuchs@gmx.net

QuestionSetColWidthmembercalmman.yang6-Mar-13 22:29 
What SetColWidth unit is it?  After the test, SetColWidth (1) is approximately equal to 0.33 pixels in excel. Why not pixels it?
AnswerRe: SetColWidthmemberRuth Ivimey-Cook3-Apr-13 1:26 
The units are the units that Excel uses, and if you look at the OpenOffice.org Excel Spec - the closest we have to a proper spec for this format - you will see that the units are essentially unspecified... nobody knows.
 
So: Experiment until you find something that works, remembering that MFC applications like Excel like to use "twips" - twentieths of a point - as a base graphical unit.
 
Ruth
Ruth Ivimey-Cook
VS2005 / C# & VB

Questionmingw32 buildmembercodcode1-Mar-13 4:44 
Has somebody successfully built the library with mingw32?
 
I get lots of errors like
include/windef.h:229:23: error: conflicting declaration 'typedef long unsigned int DWORD'
include/winsock2.h:1183:54: error: 'LPGUID' was not declared in this scope
 
any clue?, thanks
AnswerRe: mingw32 buildmemberJose Del Castillo17-Jun-13 8:09 
Hi
ExcelFormat CodeBlocks 12.11 (MingW32): compiled in Debug/Released 0 errors/0 warnings Wink | ;)
 
http://www.mediafire.com/download/3gizzfjp0zcbx4f/NewExcelFormat.rar[^]
 
Regards
QuestionA problem about ULONG Workbook::SharedStringTable::RecordSize() which may call coredump when file is large and with many same string [modified]memberrain_nov23-Jan-13 22:38 
First of all, thank you for your excellent library.

PS: this problem only exist in Linux system, it's strange, I have not figure it out .

 
Recently, I got a coredump problem. Then I analyzed it with valgrind and found there are some "Invalid write " in BasicExcel::Save call stacks. And I think data size which BasicExcel malloced dose not match the actually data size which be writed.
 
After a few time of work, I found "Workbook::SharedStringTable::RecordSize()" return the SharedStringTable size as follow:
ULONG Workbook::c::RecordSize()
{
    ULONG dataSize = DataSize();
    return recordSize_ = dataSize + 4*(dataSize/8224 + 1);
}
 
but it does not full match the all branchs, when "Workbook::SharedStringTable::continueIndices_" is't empty, data size returned by this funciton does not equal to the size which "Workbook::SharedStringTable::write" returned, finally, this error will call wrong memory wirte, even program crash.
 
Here is a patch for "Workbook::SharedStringTable::RecordSize()", I have verifed it in my enviroment:
ULONG Workbook::SharedStringTable::RecordSize()
{
	ULONG dataSize = DataSize();
	return recordSize_ = dataSize + ((continueIndices_.size() == 0) ? (4*(dataSize/8224 + 1)) : 4*((continueIndices_.size() + 1)));
}


modified 24-Jan-13 5:34am.

QuestionRegarding reading of formulamemberrohit703318-Jan-13 2:18 
Hi, I am new to VC++. I was implementing ExcelFormat but I am stuck at extracting or reading formula data from excel file. The excel input file has formula data in various cells but if I can get only the values from these cells, my work will be done. There are explicit examples to read string, int or double, but no example for reading formula values. Plz Help.
 
Rohit
QuestionI get wrong number of sheets with "xls.GetTotalWorkSheets()"memberMontexi4-Jan-13 0:16 
Hello everybody and happy new year !
 
I create an XLS file, for instance with 23 sheets. But the function "xls.GetTotalWorkSheets()" tells me there are 17 sheets.

Just to see, I tried to add a 24th sheet with "EOF" written in the first cell. It works and the function tells me now there are 24 sheets.

It's ok but with other files, it's the contrary : if I put an additional sheet with "EOF" I have a wrong number of sheets (17 instead 24 for instance) but the good number of sheets without "EOF" written in an additional sheet.

I can't predict when I have to put or not EOF and I don't understand why I have to do that (it seems not to be a good solution anyway).

I hope it's clear.

For information I use Excel 2010 and the XLS file I create is saved as a 97-2003 xls file.

If someone can help me, thank you.
QuestionHow can I choose other pages of an excel doc ?memberdima.gordeev21-Dec-12 9:22 
Dear author.
Dear users.
 
Can I choose other pages of an excel doc, or Do I not ?
Can you explain me it ?
 
--
thanks & have a nice day!
AnswerRe: How can I choose other pages of an excel doc ?memberMinnArkie22-Dec-12 2:11 
If you are referring to accessing different sheets, then as in the example above you access sheet1 like
BasicExcelWorksheet* sheet = xls.GetWorksheet(0);
 
So to access sheet2 it would be
BasicExcelWorksheet* sheet2 = xls.GetWorksheet(1);

BugBug in Merge Cellmembergodfuture19-Nov-12 19:26 
Dear Author:
 
    Thank your for providing such a excellent labrary.
 
I use it in my project, and I found when merge large number of cells, some wrong happened——part of the cells were not properly merged.
 
After my colleague read the source code , he changed the function ULONG Worksheet::MergedCells::Write(char* data) in BasicExcel.cpp like this, and it seems work well:
 
ULONG Worksheet::MergedCells::Write(char* data)
{
	ULONG bytesWritten = 0;
	size_t mergedCellsLeft_ = mergedCellsVector_.size();
	short maxPackedMergedCells;
 
	int pos = 0;//Add
	while(mergedCellsLeft_) {
		if (mergedCellsLeft_ > 1027)
			maxPackedMergedCells = 1027;
		else
			maxPackedMergedCells = (short) mergedCellsLeft_;
 
		short code = CODE::MERGECELLS;
		LittleEndian::Write(data, code, bytesWritten, 2);
		bytesWritten += 2;
		LittleEndian::Write(data, maxPackedMergedCells * 8 + 2, bytesWritten, 2);
		bytesWritten += 2;
		LittleEndian::Write(data, maxPackedMergedCells, bytesWritten, 2);
		bytesWritten += 2;
 
		for(short i = 0; i < maxPackedMergedCells; ++i)
			bytesWritten += mergedCellsVector_[i + pos].Write(data+bytesWritten);//Change

		pos += maxPackedMergedCells;//Add
		mergedCellsLeft_ -= maxPackedMergedCells;
	}
 
	return bytesWritten;
}

Questionproblem with double accuracymemberMember 955211914-Nov-12 13:17 
OK, I've looked through all the other questions here and my problem is exactly the same as a couple other questions, but the solution given doesn't seem to solve it...What am I missing?
 
Here is the pertinent code:
 
BasicExcel xls2;
	xls2.New(1);
	BasicExcelWorksheet* sheet2 = xls2.GetWorksheet(0);
	XLSFormatManager fmt_mgr(xls2);
	CellFormat fmt_double(fmt_mgr);
	fmt_double.set_format_string(XLS_FORMAT_DECIMAL);
	double amount;
	.
	.
	.
	sheet2->Cell(row2,4)->SetDouble(amount);
	sheet2->Cell(row2,4)->SetFormat(fmt_double);
 
given the following inputs:
33319.55
199330.40
134268.04
5660.10
8410.50
8069.70
33200.40
7043.60
 
I get these results:
33319.52
199329.92
134267.84
5660.10
8410.50
8069.70
33200.40
7043.60
 
Thanks in advance for any help.
AnswerRe: problem with double accuracymemberMember 955211916-Nov-12 2:54 
OK, I found the solution over at the original thread of BasicExcel...thanks to CAKPetig for the his comment of "Double to RKValue conversion code".
I changed the two functions in BasicExcel.cpp as shown below and the declaration for the first in BasicExcel.hpp needed to be changed from LONG to INT as return value.
 
int GetRKValueFromDouble(double value)
{
    if ((int(value)<<2)==value*4.0)
      return (int(value)<<2)|2; // stored as integer

    if ((int(value*100)<<2)==value*400.0)
      return (int(value*100)<<2)|3; // stored as integer/100

    union
    {
        LONGINT intvalue_;
        double doublevalue_;
    } intdouble;
 
    intdouble.doublevalue_ = value;
    if (!(intdouble.intvalue_ &0x3ffffffff))
      return (intdouble.intvalue_>>32)&~3;
 
    intdouble.doublevalue_ = value*100;  // store as multiplied value
    return (intdouble.intvalue_>>32)&~3|1;
}
 
bool CanStoreAsRKValue(double value)
{
    if ((int(value)<<2)==value*4.0) return true; // ok, stored as integer

    if ((int(value*100)<<2)==value*400.0) return true; // ok, stored as integer/100

    union
    {
        LONGINT intvalue_;
        double doublevalue_;
    } intdouble;
 
    intdouble.doublevalue_=value;
    if (!(intdouble.intvalue_ &0x3ffffffff)) return true;
 
    intdouble.doublevalue_=value*100;
    if (!(intdouble.intvalue_ &0x3ffffffff)) return true; // ok if multiplied with 100

    return false;
}

GeneralMy vote of 5memberlosinggeneration25-Sep-12 6:45 
Works great for what I needed (quick reading of large binary XLS files)
SuggestionAdd openmode to BasicExcel::Loadmemberlosinggeneration25-Sep-12 6:41 
Basically I ran into the following situation. If the file's attributes are set read-only, BasicExcel Load is going to fail (similarly, I think if it's write only it'll fail as well.) I propose adding openmode to the constructor and Load of BasicExcel to make using that directly easier.
basically this:
class BasicExcel
{
...
	BasicExcel(const char* filename, ios_base::openmode mode=ios_base::in|ios_base::out);
...
	bool Load(const char* filename, ios_base::openmode mode=ios_base::in|ios_base::out);
	bool Load(const wchar_t* filename, ios_base::openmode mode=ios_base::in|ios_base::out);
 
Other than that, it works great. Thanks for updating, improving, maintaining, etc. If you're interested, I was able to do what I needed in about 21 lines of code. I had a fairly large xls (roughly 25 cols & 11k rows) that PHPExcel took 70+ seconds to read, I tried a couple others in PHP and was only able to get it down to about 22 or so seconds to read. This reads it in <5 seconds. So now what I'm able to do is call this small program that spits out a CSV to do the rest of the parsing in PHP without the huge performance hit that the PHP libraries had. Smile | :)
QuestionBug or wrongly buildmemberkrasimir.hristov19-Sep-12 22:11 
Hallo Everybody,
 
Did I build the project wrongly or did I hit a bug?
 
I’ve downloaded the project, simply opened with MSDEV and built it. The example worked fine. Then I’ve changed the number of rows for the big file to 5000:
 
write_big_sheet("big-example.xls", 5000, 100);
 
In Debug build I got some memory related exception (DAMAGE: after XXXX block (#YYYY) at ZZZZZ). The .xsl file is generated but Excel shows no data in it.
 
I’m using VC6.0; Windows XP, Excel 2007.
 
Looking forward for your help.
 
Regards,
Krasimir Hristov
QuestionFormulas that reference other sheetsmemberbeaker200518-Sep-12 10:55 
Hi Martin,
 
First off, many thanks for the hard work, this library is great! Big Grin | :-D
 
However, I do have one minor issue. The library does not seem to work with formulas if they reference cells in a different sheet via the use of the ! operator.
 
For example, consider a workbook with 2 sheets, named "Sheet1" & "Sheet2".
 
"Sheet1:A1" contains the formula: =Sheet2!A1 // note the use of the ! operator
 
"Sheet2:A1" contains the value: 10
 
In this example, "Sheet1:A1" should display the value looked up in "Sheet2:A1", i.e. 10
 
This works in Excel.
 
If you do a simple load and save of the xls file with these contents through your library, the saved xls file has broken the formula when re-opened in Excel.
 
I imagine the code is already in the library to create a simple fix for this without too much effort?
 
Regards,
 
Beaker.
GeneralMy vote of 5memberKindYAK29-Aug-12 1:05 
This project helped me a lot in my research activities.
Thanks!
QuestioncolorsmemberSuwabaraKai20-Aug-12 23:17 
Hi, thanks for this very nice library!
 
Very easy to build, to use, with good examples.
Is it possible to use colors others than the colors in EXCEL_COLORS enum?
I would like to use a custom color defined by a rgb code.
 
And if not, is there an easy way to modify the library to allow it?
 
Thanks
AnswerRe: colorsmemberRuth Ivimey-Cook3-Apr-13 1:35 
The simple answer is that the library can't be modded to allow features that Excel doesn't allow itself. Excel uses a specific colour palette for colours: the only way to change the colour used would be to define a new colour in the palette. See record PALETTE record type (not currently read by the library).
 
Bear in mind that this feature is only available in Excel 2003 and even then is only available to VBA:
change-cell-background-color-using-vba-HA001136627.aspx
Ruth Ivimey-Cook
VS2005 / C# & VB

Generalwhy got a leak [modified]memberhejiasheng27-Jul-12 5:43 
I imported the demo project into VS2010. If I uncomment the following line, I got many leak errors (example_read_write). Anybody can help me? Thanks.
 
_CrtSetDbgFlag(_CrtSetDbgFlag(0)|_CRTDBG_LEAK_CHECK_DF);
 
When I migrate these source code into a dialog-based application, the same thing occures even if this line is commented.

modified 28-Jul-12 1:07am.

Questionto add datetimememberrusvodo3-Jul-12 1:43 
to use datetime value need to use double value like thisSmile | :)
string code give value with ' like '24:22
 
CellFormat fmt(fmt_mgr);
fmt.set_format_string(XLS_FORMAT_DATE);
 
double _utext = (double)1/24*hours + (double)1/24/60*minuts;
 
sheet->Cell(i+1, col)->Set(_utext);
sheet->Cell(i+1, col)->SetFormat(fmt);
Questioncrash on trying to load a xls file created by ExcelFormat Lib on 64 bit win7 compiled as 32 bitmemberMember 855906630-May-12 9:43 
I am trying to use the code to :
--->create a xls file with the first row as headers
--->close the file
--->in a loop open and read in the file (xls.Load(filename))
--->write out the next row
The program crashes after creating the file with headers and trying to read in the xls file.
it crashes at: Read(&*(data.begin()), data.size());
then:
reference operator*() const
{ // return designated object
return ((reference)**(_Mybase *)this); <<-- here
}
then finally:
reference operator*() const
{ // return designated object
#if _ITERATOR_DEBUG_LEVEL == 2
if (this->_Getcont() == 0
|| this->_Ptr == 0
|| this->_Ptr < ((_Myvec *)this->_Getcont())->_Myfirst
|| ((_Myvec *)this->_Getcont())->_Mylast <= this->_Ptr)
{ // report error
_DEBUG_ERROR("vector iterator not dereferencable"); <<--- here
_SCL_SECURE_OUT_OF_RANGE;
}
Any thoughts???
Thanks Mitch
AnswerRe: crash on trying to load a xls file created by ExcelFormat Lib on 64 bit win7 compiled as 32 bitmemberMember 95296302-Nov-12 8:30 
do you find out how to fix it?
Questionalt + entermemberlilesh29-May-12 3:16 
hi plz tell me how to add alt + enter in cell to write on new line
AnswerRe: alt + entermembertrante27-Sep-12 2:29 
I also have this problem.
Can you recommend the solution please.
QuestionDate field converts to a 6 digit numbermemberArunCherian20-Apr-12 13:25 
First of all, great library...
 
I was converting a sheet, it has a Date column, when I write it out to a text file it comes as a number... can you please let me know why?
 
Thanks!
Questionkeeping scaling values in xls file?memberFred D.2-Apr-12 2:31 
I have an Excel file which has a scaling factor of 1 to 1 (fit all in one page).
When I open it, make some update data and save (using SaveAs()) it loses this parameter.
I have had a look in the excel lib but I cannot anywhere in tere where this parameter would be taken into account.
 
Any pointer on this would be great, thanks.
Fred
Questionformula result?memberFred D.28-Mar-12 20:27 
is it possible to get a formula result once the xls file has been loaded?
 
I've noticed a _result field in BasicExcellCell but it doesnt seem to get ever set to any kind of valid result.
 
Also this variable is set to be
unsigned char _result[8];
limiting the result size to 8 chars?? why is this?
 
tx.
Questionhow to add a Hyperlink in Excel?membervinkong22-Mar-12 21:26 
how to add a Hyperlink in Excel?
Bugbug in open big file[fixed]memberliyouchang18-Mar-12 17:39 
Dear Author:
 
Thank your for providing such a excellent labrary.
I use it in my project, and I found when read big file it will crash.By debugging the code,I found when the sst field have a continued record the program will crash certainly.
 
I change the Workbook::SharedStringTable function and the LargeString::ContinueRead function the fix this problem.
 

ULONG Workbook::SharedStringTable::Read(const char* data)
{
	Record::Read(data);
	LittleEndian::Read(data_, stringsTotal_, 0, 4);
	LittleEndian::Read(data_, uniqueStringsTotal_, 4, 4);
	strings_.clear();
	strings_.resize(uniqueStringsTotal_);
	ULONG npos = 8;
	if (continueIndices_.empty()) {
		for(ULONG i=0; i<uniqueStringsTotal_; ++i)
			npos += strings_[i].Read(&*(data_.begin())+npos);
	} else {
		// Require special handling since CONTINUE records are present
		ULONG maxContinue = (ULONG) continueIndices_.size();
		for(ULONG i=0, c=0; i<uniqueStringsTotal_; ++i) {
			char unicode;
			ULONG stringSize;
			LittleEndian::Read(data_, stringSize, npos, 2);
			LittleEndian::Read(data_, unicode, npos+2, 1);
 
			int multiplier = unicode & 1 ? 2 : 1;
			if (c >= maxContinue || npos+stringSize*multiplier+3 <= continueIndices_[c]) {
				// String to be read is not split into two records
				npos += strings_[i].Read(&*(data_.begin())+npos);
				// A sst field is end just with a record end
				if (npos ==continueIndices_[c] )
					c++;
			} else {
				// String to be read is split into two or more records
				int bytesRead = 2;// Start from unicode field
				int sstHead=1;	//unicode field len
				if (unicode & 8) sstHead += 2;	//cRun filed len
				if (unicode & 4) sstHead +=4; //cbExtRst  filed len

				int size = continueIndices_[c] - npos  - bytesRead - sstHead;
				++c;
				if (size > 0) {
					size /= multiplier; // Number of characters available for string in current record.
					bytesRead += strings_[i].ContinueRead(&*(data_.begin())+npos+bytesRead, size,false);
					stringSize -= size;
					size = 0;
				}
				while(c<maxContinue && npos+stringSize+1>continueIndices_[c]) {
					ULONG dataSize = (continueIndices_[c] - continueIndices_[c-1] - 1) / multiplier;
					bytesRead += strings_[i].ContinueRead(&*(data_.begin())+npos+bytesRead, dataSize,false);
					stringSize -= dataSize + 1;
					++c;
				}
				if (stringSize > 0) 
					bytesRead += strings_[i].ContinueRead(&*(data_.begin())+npos+bytesRead, stringSize);
				npos += bytesRead;
			}
		}
	}
	return npos + 4*(npos/8224 + 1);
}
 

ULONG LargeString::ContinueRead(const char* data, int size,bool toEnd)
{
	if (size == 0)
		return 0;
	char unicode;
	USHORT richtext;
	ULONG phonetic;
	LittleEndian::Read(data, unicode, 0, 1);
	ULONG npos = 1;
	if (unicode & 8) {
		LittleEndian::Read(data, richtext_, npos, 2);
		npos += 2;
	}
	if (unicode & 4){
		LittleEndian::Read(data, phonetic_, npos, 4);
		npos +=4;
	}
	if (unicode_ == -1)
		unicode_ = unicode;
	if (unicode_ & 1) {
		// Present stored string is uncompressed (16 bit)
		size_t strpos = wname_.size();
		wname_.resize(strpos+size, 0);
		if (unicode & 1) {
			LittleEndian::ReadString(data, &*(wname_.begin())+strpos, npos, size);
			npos += size * SIZEOFWCHAR_T;
		} else {
			// String to be read is in ANSI
			vector<char> name(size);
			LittleEndian::ReadString(data, &*(name.begin()), npos, size);
			mbstowcs(&*(wname_.begin())+strpos, &*(name.begin()), size);
			npos += size;
		}
	} else {
		// Present stored string has character compression (8 bit)
		size_t strpos = name_.size();
		name_.resize(strpos+size, 0);
		if (unicode & 1) {
			// String to be read is in unicode
			vector<wchar_t> name(size);
			LittleEndian::ReadString(data, &*(name.begin()), npos, size);
			wcstombs(&*(name_.begin())+strpos, &*(name.begin()), size);
			npos += size * SIZEOFWCHAR_T;
		} else {
			LittleEndian::ReadString(data, &*(name_.begin())+strpos, npos, size);
			npos += size;
		}
	}
	if (toEnd)
	{
		if (richtext_)	npos += 4*richtext_;
		if (phonetic_)		npos += phonetic_;
	}
	return npos;
}

GeneralRe: bug in open big file[fixed]memberMember 408905620-Mar-12 0:54 
I tried with your new code. But still failed when read in the excel with 180 rows and
3 columns. the file size is 26112. My environment is under Linux 32-bits version.
If you have full package, please send to my email for me to try again.
Thanks for your help Smile | :)
BugBug found, xls attached [modified]memberdsadljsadlk2323-Feb-12 22:29 
For a first of all, of course, thank you for the beautiful and compact parser. My gosh, I just even still can't believe that people writes such tools and components just for fun and then supports it ! I studied within this code a little. I've found a bug which used to appear during processing of a quite large XLS files. This is a two purposes: no range checks (i mean, data+dataSize) inside almost all ::Read() routines of XLS objects (such as worksheet, cell, etc.) and, perhaps, obj reader error somewhere in workbook reader functions code. I attached an XLS file on which the described error is appeared here: http://www.2shared.com/document/zP3aAwxM/price-standart-zabor.html
(russian xls document)
I also tried a version of source that is attached to this article, at the top of page ('download demo source') and I tried the last version downloaded from SVN /trunk..
Call stack would be:
BasicExcel::BasicExcel() -> BasicExcel::Load() -> BasicExcel::Read() -> Workbook::Read() -> Record::Read()[a lot of hundred times#] -> ... memmove().
 
PS.: XLS file that I attached is OK when parsed using Excel, OpenOffice Math and OffVis tool.
 
PSS.: By the way, I used to write CFB (Compound file) parser by myself using C and I will add this approach to
the CompoundFile abstraction in addition of delegating calls to StgXxx() APIs. Lemme know if you want the code and I send it to you or upload to some place.

modified 24-Feb-12 5:48am.

QuestionVersion 2.4membermarco7022-Feb-12 10:45 
Very good Job!
Where can be found Version 2.4 of this library?
AnswerRe: Version 2.4membermarco7022-Feb-12 23:11 
Smile | :) Just found at http://shell.franken.de/svn/sky/excel/trunk/ExcelFormat
QuestionBug in Cell Formatting?memberwdolson17-Feb-12 23:58 
I'm not sure where the problem is, but some cell formats are only quasi recognized by Excel. I haven't narrowed down exactly what's causing it, but with some cell format combinations, if you select the cell and got to Format->Cells, Excel does nothing. No error message, no cell formatting box, just nothing happens.
 
I discovered this behavior in the example. If you open example1.xls and select one of the blue background cells, you will see this behavior (at least with Excel 2000, I don't have a newer version installed, but the BIFF format has been the same since Excel 1997 as far as I can tell, so this shouldn't matter).
 
I've run into the same problem with several other file formats too. It looks like the problem is with some combination of cell formats rather than just a given format.
 
Has anybody else seen anything like this?
 
Edit: I've seen this behavior both with setting borderlines and setting a background color. Removing both of those appears to allow further editing of the cell by users in the produced file.
 
Bill
AnswerRe: Bug in Cell Formatting? [modified]memberMikeGrain20-Mar-12 14:43 
I get the same problem... for me it does seems to be related to using the EXCEL_LS_NO_LINE style

modified 20-Mar-12 21:06pm.

GeneralRe: Bug in Cell Formatting?memberwdolson20-Mar-12 15:21 
I was using EXCEL_LS_THIN. It looks like setting any line style causes problems.
 
Bill
GeneralRe: Bug in Cell Formatting?memberMikeGrain20-Mar-12 19:25 
I'm also having a problem with using the cell format buttons (currency, percent,...) on the toolbar. Clicking on any of those buttons, I get a style not found error. I have not been able to figure that one out either.
GeneralRe: Bug in Cell Formatting?memberwdolson21-Mar-12 16:00 
I don't think there is all that much support for this anymore. I don't think it has been updated in a while.
 
Maybe someday I'll have the time to go back and figure out what's going on, but for now, I'm leaving that formatting out of my program.
 
Bill
QuestionName Manager SupportmemberRohit Dubey from Hyderabad16-Feb-12 8:40 
If you have a sheet with named cells they are destroyed much like images when you edit the contents and resave them.
 
Is there any plans for support for this feature and if not can you point me in the right direction for adding defined names. It has something to do with the globals substream but beyond that it's all Greek to me.
GeneralGood jobmemberhuabuyu12-Feb-12 16:30 
Smile | :) Thank you! It's wonderful!
QuestionSmartPtr problemmemberMember 80301307-Feb-12 9:05 
Hallo Martin,
 
First of all thanks for this excellent project. I can use it as a basis for generating excel sheets from template excel files.
But I ran into some problems.
Consider the following small testprogram:
 
BasicExcel xls;
xls.New(1);
xls.AddWorksheet(0);
xls.DeleteWorksheet(0);
xls.AddWorksheet(0);
 
Problems arise when inserting/deleting a worksheet that is not at the end of the array,
in the following functions:
 
BasicExcelWorksheet* BasicExcel::AddWorksheet(const char* name, int sheetIndex)
{
...
...
yesheet = &**(yesheets_.insert(yesheets_.begin()+sheetIndex, new BasicExcelWorksheet(this, sheetIndex)));
...
...
}
 
bool BasicExcel::DeleteWorksheet(int sheetIndex)
{
...
...
yesheets_.erase(yesheets_.begin()+sheetIndex);
...
...
}
 
After some digging, I found that the _ref_cnt used in SmartPtr was decremented too many times, which caused the BasicExcelWorksheet object to be deleted.
I could work around these problems by manipulating the _ref_cnt value in the functions above, but I think that is not the most elegant way to do.
May be you can give me a solution.
Thanks.
 
Jaap Dijkstra

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130617.1 | Last Updated 3 Feb 2011
Article Copyright 2009 by Martin Fuchs
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid