对于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函数是随手写的,存在内存泄露。
因为这里面很多代码是从项目中提取了,为了体现例子的简介性,删除了所有的异常处理流程,仅仅保留了和具体数据有关的内容。
希望我的这篇文章能对您的工作带来方便,欢迎讨论,拒绝无理板砖。
- 对于EXCEL2003的XML格式文件的解析
- Java 语言对XMl 格式文件的 纯解析
- VC对于XML的解析以及操作
- VC对于XML的解析以及操作
- 简单的读取xml格式文件
- XML格式文件解析
- 关于WMV格式文件的解析
- 将Excel2003的xls格式文件转换为kml及gpx文件(ExcelToKml)
- 对于带有表空间xmlns的xml文件的解析
- Java生成和解析XML格式文件和字符串的实例代码【dom4j中的SAXReader对象读取并解析xml文件】
- Java生成和解析XML格式文件和字符串的实例代码
- Java生成和解析XML格式文件和字符串的实例代码
- Java生成和解析XML格式文件和字符串的实例代码
- 【一周工作总结】循环调用接口,和一些Xml格式文件的解析。
- 生成和解析XML格式文件
- 对于解析的理解
- 解析csv格式文件的一段C代码
- JQuery解析不同格式文件的数据
- Boyer-Moore算法学习
- c# 获得QQ聊天输入框中的内容(原创:半水之剑)
- 网站想长期发展必须懂得平衡流量来源
- 数据库自增字段
- 几种软件开发模型介绍
- 对于EXCEL2003的XML格式文件的解析
- 陶陶书评 之 应需而变--设计的力量
- Sql Server设置外键约束和IIS支持flv文件
- 谈一下个人对百度相关搜索的见解
- [长大]_公益-腾讯月捐,顺便记录一下开通财付通
- 网站优化 要的就是细节
- win下Apache修改主目录
- sigaction 函数说明
- 浅谈站外优化的几点常用方法