对于EXCEL2003的XML格式文件的解析

来源:互联网 发布:崩坏三初始号淘宝 编辑:程序博客网 时间:2024/06/06 08:59

标 题: 对于EXCEL2003的XML格式文件的解析
作 者: Sdoat

时 间: 2010-09-20,16:41:00

链 接: http://blog.csdn.net/sdoat/archive/2010/09/20/5897404.aspx

首发CSDN,欢迎转载,转载请注明来源,谢谢!

工作中,很多时候用到Excel处理分析数据,而更深层次的数据挖掘,可能需要编写专门的程序进行处理,这时候有一个问题,就是如何从EXCEL中读取数据。

从EXCEL中读取数据,有多种方法,本文仅仅探讨文本读取的方式。EXCEL支持多种存储方式,从XLS到TXT到XML到....,本文仅仅探讨从EXCEL2003中的XML格式中读取指定数据。

 

本文环境:XP SP2  + VC6.0  + MS Office2003

本文工具:EXCEL2003 + VC6.0 + UE15.0

 

首先,分析一下XML的文件格式。

使用UE打开XML文件,可以看到,文件开头是XML文件头信息。

其中<Workbook>是EXCEL文件的内容;

<DocumentProperties>、<OfficeDocumentSettings>、<ExcelWorkbook>是文档的属性,本文重点是读取内容,故这些信息统统忽略;

<Styles>是EXCEL中段落、文字所有的属性,<Style>中是每一种不同的属性,所有的这些属性都有自己的id,此处也不是重点,也忽略;

<Worksheet>是工作表的信息,EXCEL文件中有几个工作表,此处就会有几个<Worksheet>的信息,所有的数据都存放在这些<Worksheet>中,我们的解析也主要以<Worksheet>为主。

 

<Worksheet ss:Name="工作表名">,此处可以获得工作表名,EXCEL中工作表必须有名字,故此处必定不为空。

接来下是一个<Table>,在<Table>中可以读取到此工作表最大的行数和列数,其存储形式为

<Table ss:ExpandedColumnCount="5" ss:ExpandedRowCount="63" x:FullColumns="1"
   x:FullRows="1" ss:DefaultColumnWidth="54" ss:DefaultRowHeight="14.25">

这表明,此张工作表,最大列数为5,最大行数为63,其它为大小控制信息,忽略;

 

接下来有一系列的<Row>,这个工作表,总共有63个,而每个<Row>里面最多有5个<Cell>。

<Row>中存放的是一行的数据,而<Cell>中存放的是行中每一个有有效数据的格子的数据。

 

我们解析的时候,先根据<Row>读取每一行数据,然后再根据<Cell>解析出每一个格子的数据。

而<Row>中第一个<Cell>中可能存在下面数据    ss:Index="5",这个代表这一行中第一个有有效数据的格子是第5个格子,也就是说这一行的前四个数据都是空,我们还会看到   ss:StyleID="m153114702" ,这是代表这个单元格的属性,如果想知道这个属性是什么,请在UE中搜索m153114702,然后去看文件最前面的那个搜索结果,里面有定义,不过,此文忽略,

然后通常情况下,我们会看到一个<Data>,这里面就存储着我们需要的数据,但是这个<Data>可能也存在控制信息,此处控制信息同样忽略,我们只要找到<Data>的最后,就能找到数据的开始了,然后再找到</Data>,就找到数据的结尾了。

 

此处有一个重要的问题就是,如果一行数据太长,XML文件会分行,并且为了缩进,加入了,很多空格。所以在处理数据的时候,可能需要将这些多余的回车和空格去除,这样才能使用字符串比较函数。

 

此时数据分析结束,下面,我们将使用程序将工作表中的数据读取出来。

代码有点儿长,但是包含一个完整的例子。

 


#include <stdio.h>
#include <afxwin.h>

// 从文件中读取数据
// 数据以 /0 结尾
LPSTR MReadDataFromFile(LPCSTR fileName)
{
    CFile dataFile;

    if(!dataFile.Open(fileName, CFile::modeRead))   // 只读方式打开数据,不能以共享方式打开
    {
        TRACE(_T("Can't Open The Data File, Pls Check!/n"));
        return NULL;
    }

    DWORD lDataLen = dataFile.GetLength() + 1;      // 数据长度为文件长度+1
   
    TRACE(_T("MReadDataFromFile data length = %d/n"), lDataLen - 1);
   
    LPSTR pDataBuf   = new CHAR[lDataLen];
    if(pDataBuf == NULL)                            // 容错
    {
        TRACE(_T("MReadDataFromFile 1 申请内存失败!/n"));    
        return NULL;
    }
   
    memset(pDataBuf, 0, lDataLen);                  // 清零
   
    dataFile.Read(pDataBuf, dataFile.GetLength());  // 读取
    dataFile.Close();
   
    return pDataBuf;
}

// 删除gDataBuf中所有的 空格、Enter 字符
LPSTR MDeleteAllSpaceEnter(LPSTR str)
{
    ASSERT(str != NULL);
   
    UINT strNewLen = strlen(str) + 1;
   
    LPSTR strNew   = new CHAR[strNewLen];   // 开辟新的buffer
    if(strNew == NULL)
    { 
        return NULL;
    }
    memset(strNew, 0, strNewLen);
   
    LPCH dest = strNew;
    LPCH src  = str;
    LPCH destEnd = strNew + strNewLen;
   
    for (; *src != NULL; src++)     // 逐个字符删除
    {
        if(*src == ' ') continue;
        else if(*src == '/r')   continue;
        else if(*src == '/n')   continue;
        *dest = *src;
        dest++;
    }
   
    return strNew;
}


typedef struct SheetNode_t
{
    LPSTR   ptr;    // 表的指针
    LPSTR   name;   // 表名
    UINT    col;    // 列数
    UINT    row;    // 行数
}SheetNode, SN, *PSN, **PPSN;     // 表的信息

// 从数据中获取表的节点
// 获取的信息存放在 pSheet 中
// pSheet以全0数据结尾
PSN MGetSheetNode(LPSTR str)
{
    LPSTR strSheet = str;
    LPSTR strBegin, strEnd;
    UINT i, j;
   
    // 计算实际上表的数量
    for(i = 0; ((strSheet = strstr(strSheet, "<Worksheetss:Name=/"")) != NULL); i++)
    {
        strSheet += 10;
    }
   
    TRACE(_T("MGetSheetNode Sheet number = %d/n"), i);
    PSN pSheet = new SN [i + 1];
   
    if(pSheet == NULL)
    {
        TRACE(_T("MGetSheetNode 1 申请内存失败!/n"));
        return NULL;
    }
   
    memset(pSheet, 0, sizeof(SN) * (i + 1));
   
    // 构建各个工作表的数据区域
    strSheet = str;
    for(i = 0; ((strSheet = strstr(strSheet, "<Worksheetss:Name=/"")) != NULL); i++)
    {
        *(strSheet - 1) = 0;
        pSheet[i].ptr = strSheet;
        strSheet += 10;
    }
   
    // 获取各个工作表的表名、行数、列数
    for (j = 0; j < i ; j++)
    {
        strBegin = pSheet[j].ptr + strlen("<Worksheetss:Name=/"");
        strEnd = strstr(strBegin, "/">");
        pSheet[j].name = new CHAR[strEnd - strBegin + 1];
        memset(pSheet[j].name, 0, (strEnd - strBegin + 1) * sizeof(CHAR));
        strncpy(pSheet[j].name, strBegin, (strEnd - strBegin));  // 获取表名
       
        strBegin = strstr(pSheet[j].ptr, "ExpandedColumnCount=/"");       
        sscanf(strBegin, "ExpandedColumnCount=/"%d/"", &pSheet[j].col);  // 获取列数
       
        strBegin = strstr(pSheet[j].ptr, "ExpandedRowCount=/"");
        sscanf(strBegin, "ExpandedRowCount=/"%d/"", &pSheet[j].row);     // 获取行数
       
        TRACE(_T("MGetSheetNode Sheet%d name: %s, <row, col>= <%d,%d>/n"), j, pSheet[j].name, pSheet[j].row, pSheet[j].col);
    }
    return pSheet;
}

#define MAX_LEVEL   6

#ifndef BUFF_SIZE
#define BUFF_SIZE   256
#endif  // BUFF_SIZE

typedef struct SheetRow_t   // 存放一行数据
{
    LPSTR   row;
}SheetRow, SR, *PSR, **PPSR;

typedef union GridTable_t   // 存放解析后格子中的数据信息
{
    CHAR    description[MAX_LEVEL][BUFF_SIZE];      // 通用表格
}GridTable, GT, *PGT, **PPGT;


// 获取工作表中全部行的数据
// *pRow 为存放行数据的指针
// 此指针动态分配空间,但是内部不释放
void MGetSheetRow(SN &sheet, PPSR ppRow)
{
    *ppRow = new SR[sheet.row];
    if(*ppRow == NULL)     // dynamic malloc memory
    {
        TRACE(_T("MGetSheetRow  申请内存失败!/n"));
        return ;
    }
   
    UINT i = 0;
    LPSTR strBegin;
    PSR &pRow = *ppRow;
   
    strBegin = sheet.ptr;
   
    for(i = 0; i < sheet.row; i++)  // 最多只查找表标明的行数
    {
        strBegin = strstr(strBegin, "<Row");   // 查找每一行的行头关键字
        if(strBegin == NULL)        // 如果没有找到行头关键字,认为行数已经查找完毕
        {
            break;
        }
        *(strBegin - 1) = 0;            // 将前一行划出去
        pRow[i].row = strBegin;
        strBegin += 10;
    }
   
    TRACE(_T("MGetSheetRow Row Theory = %d, Actually= %d!/n"), sheet.row, i);
}

// 解析行数据
// 将一行中的每一格数据分割开
// 如果数据太长,则会只保存某一部分以防止溢出
BOOL MAnalysisRow(GT &rt, LPCSTR row, UINT colNum)
{
    LPSTR strBegin = (LPSTR)row;
    LPSTR strTemp = NULL;
    LPSTR strEnd = NULL;
    LPSTR strNextBegin = NULL;
    LPSTR strBeginBak = NULL;
    UINT i = 0, k;
   
    memset(&rt, 0, sizeof(GT));     // 初始化数据
   
    strBegin = strstr(strBegin, "<Cellss:");
    if(strBegin == NULL) return TRUE;
    strNextBegin = strstr(strBegin + 1, "<Cellss:");
    if(strNextBegin == NULL)    strNextBegin = (LPSTR)(UINT)(-1);
    strTemp = strstr(strBegin, "<Cellss:Index=");
   
    if(strTemp  != NULL && strTemp < strNextBegin)        // 如果此行第一个有数据格子有偏移量
    {
        sscanf(strBegin, "<Cellss:Index=/"%d/"", &i);           // 修正偏移量
        i--;
    }
   
    if(i >= colNum - 1)     // 空行
    {
        TRACE(_T("This is space row!/n"));
    }
    else
    {
        TRACE(_T("This row have %d space grid in front!/n"), i);
    }
   
    for ( ; i < colNum; i++)        // 逐格读取
    {
        strBeginBak = strBegin;
        strBegin = strstr(strBegin, "<Cellss:");          // 查找格子数据标示
        if(strBegin == NULL)    break;
        strNextBegin = strstr(strBegin + 1, "<Cellss:");
        if(strNextBegin == NULL)    strNextBegin = (LPSTR)(UINT)(-1);
        strBegin = strstr(strBegin, "Datass:Type=/"");          // 查找格子数据标示
        if(strBegin == NULL)    break;
        strBegin = strstr(strBegin, "/">");                     // 数据前面内容标示
        if(strBegin == NULL)    break;
        strBegin += strlen("/">");                              // 定位到数据
        strEnd = strstr(strBegin, "<");                   // 定位到数据尾部
        if(strEnd == NULL)  break;
        if(strBegin >= strNextBegin || strEnd >= strNextBegin)
        {
            TRACE(_T("%dth grid is NULL!/n"), i);
            strBegin = strNextBegin;
            continue;
        }
        k = strEnd - strBegin;
        strncpy(rt.description[i], strBegin, k > BUFF_SIZE ? BUFF_SIZE - 1 : k);
        TRACE(_T("%dth grid is %s!/n"), i, rt.description[i]);
    }
   
    return TRUE;
}

// 获取工作表中全部格子的数据
// *pRow 为存放行数据的指针
// 此指针动态分配空间,但是内部不释放
UINT MGetSheetGrid(SN &sheet, PPGT ppGrdTbl)
{
    PSR pShtRw = NULL;
    PGT &pGrdTbl = *ppGrdTbl;
   
    MGetSheetRow(sheet, &pShtRw);     // 获得所有行的数据
   
    pGrdTbl = new GT[sheet.row];    // 存放资源表中数据
    if(pGrdTbl == NULL)
    {
        TRACE(_T("MGetSheetGrid 1 申请内存失败!/n"));
        return 0;
    }
   
    memset(pGrdTbl, 0, sizeof(GT) * sheet.row);   // init
   
    UINT i = 0, j = 0;
    for(i = 0, j = 0; i < sheet.row; i++)
    {
        if(MAnalysisRow(pGrdTbl[j], pShtRw[i].row, sheet.col))  // 逐行解析
        {
            j++;
        }
    }
   
    delete [] pShtRw;
    return j;
}

int _tmain()
{
    LPSTR pData = MReadDataFromFile(_T("增值业务清单.xml"));
    LPSTR pNew = MDeleteAllSpaceEnter(pData);
    if(pNew == NULL)
    {
        return 0;
    }
    else
    {
        delete [] pData;
        pData = pNew;
    }
    if( pData == NULL) return 0;
    PSN pSheet = MGetSheetNode(pData);
    if(pData == NULL) return 0;
    PGT pGt = NULL;
    if(0 != MGetSheetGrid(pSheet[0], &pGt))
    {
        printf("OK!/n");
    }
    else
    {
        printf("FALSE!/n");
    }
    return 0;
}

 

至此,整个XML文件解析完成,下面就可以利用解析结果进行深层次的数据处理。

PS:例子中,main函数是随手写的,存在内存泄露。

因为这里面很多代码是从项目中提取了,为了体现例子的简介性,删除了所有的异常处理流程,仅仅保留了和具体数据有关的内容。

希望我的这篇文章能对您的工作带来方便,欢迎讨论,拒绝无理板砖。