CでExcelを読む

Excelのファイルフォーマット

OLE複合ドキュメントというフォーマットの中に、BIFFと言うフォーマットが入っている。

OLE 複合ドキュメントを扱うライブラリ

http://linux.softpedia.com/get/Programming/Libraries/POLE-1528.shtml

OLE 複合ドキュメント資料

http://sc.openoffice.org/compdocfileformat.pdf

ソース

cotinueレコードに対応していないので、文字が完全に表示されず、
エラーになることもある。(データが途切れてしまうため)

#include <iostream>
#include <fstream>
#include <stdlib.h>
#include <stdio.h>
#include <list>
#include <string>
#include<iconv.h>

#include "pole.h"

void convertUnicode2Shiftjis(char *to, char *from, size_t outsiz)
{

    // iconvを開く
    iconv_t it = iconv_open("SHIFT_JIS", "UTF-16LE");
    if (it == (iconv_t)-1) {
	perror("iconv_open失敗");
	exit(1);
    }

    // 変換する
    char *outstr = to;
    const char *instr = from;
    size_t insiz = outsiz;
    size_t siz = iconv(it, &instr, &insiz, &outstr, &outsiz);
    if (siz == -1) {
	perror("iconv_open失敗");
	exit(1);
    }

    // iconvを閉じる
    iconv_close(it);
}

void dumpSST(POLE::Stream* stream, unsigned short datalength)
{

    unsigned long length = 0;
    unsigned short stringSize = 0;
    unsigned short SSTSize = 0;
    unsigned long extstrSize = 0;
    unsigned char flag = 0;
    unsigned short fontSize = 0;

    // SSTレコードとEXTSSTレコードに格納される文字列数の合計をスキップ
    stream->seek( stream->tell() + 4);

    // STTに格納される文字数
    if (stream->read( (unsigned char *)&SSTSize, 4 ) != 4) return;

    while (datalength > length)
    {
	// 文字数(バイト数ではない!!)
	if (stream->read( (unsigned char *)&stringSize, 2 ) != 2) return;
	length += 2;

	// フラグ
	if (stream->read( &flag, 1 ) != 1) return;
	length += 2;

	// フォント情報個数
	if (flag & 8) {
	    if (stream->read( (unsigned char *)&fontSize, 2 ) != 2) return;
	    length += 2;
	}

	// 拡張文字情報数
	if (flag & 4) {
	    if (stream->read( (unsigned char *)&extstrSize, 4 ) != 4) return;
	    length += 4;
	}

	// 文字列読み込み
	if (flag & 1) { // unicodeが格納されている
	    unsigned char *buffer = new unsigned char[stringSize * 2 + 2];
	    memset(buffer, 0, stringSize * 2 + 2);
	    if (stream->read( buffer, stringSize * 2 ) != stringSize * 2) return;
	    length += stringSize * 2;
	    char *out = new char[stringSize *2 + 1];
	    memset(out, 0, stringSize * 2 + 1);
	    convertUnicode2Shiftjis(out, (char *)buffer, stringSize * 2);
	    printf("%s\n", out);
	    delete[] out;
	    delete[] buffer;
	} else { // hibitをとったunicodeが格納されている
	    unsigned char *buffer = new unsigned char[stringSize + 1];
	    memset(buffer, 0, stringSize + 1);
	    if (stream->read( buffer, stringSize ) != stringSize) return;
	    length += stringSize;
	    printf("%s\n", buffer);
	    delete[] buffer;
	}

	// フォント情報スキップ
	if (flag & 8) {
	    // IDをskip
	    stream->seek( stream->tell() + 4 * fontSize);
	    length += 4 * fontSize;
	}

	// 拡張文字情報スキップ
	if (flag & 4) {
	    // IDをskip
	    stream->seek( stream->tell() + extstrSize);
	    length += extstrSize;
	}
    }
}

void dumpExcel(POLE::Stream* stream)
{
    unsigned char buffer[16];
    unsigned short recordID = 0;
    unsigned short dataSize = 0;
    while (!stream->eof()) {
	if (stream->read( (unsigned char *)&recordID, 2 ) != 2) return;
	if (stream->read( (unsigned char *)&dataSize, 2 ) != 2) return;
	if (recordID == 0xfc) { // SSTレコードのみ調べる
	    dumpSST(stream, dataSize);
	} else {
	    stream->seek( stream->tell() + dataSize);
	}
    }
}

int main(int argc, char *argv[])
{
    if( argc < 2 )
    {
	return 0;
    }

    POLE::Storage* storage = new POLE::Storage( argv[1] );
    storage->open();
    if( storage->result() != POLE::Storage::Ok )
    {
	std::cout << "Error on file " << argv[1] << std::endl;
	return 1;
    }

    std::list<std::string> entries= storage->entries( "/" );
    for(std::list<std::string>::iterator it = entries.begin(); it != entries.end(); ++it )
    {
	POLE::Stream* ss = new POLE::Stream( storage, "/" + *it );
	if (*it == "Workbook") {
	    dumpExcel(ss);
	}
	delete ss;
    }

    delete storage;

    return 0;
}