分类: C/C++
2008-04-02 14:16:07
阅读本文需要有 XML 规范基础 及 .net XML 解析基础
本文示例代码使用 Visual Stdio 2005 、Office Execl 2007
一、首先我们来看看Execl XML的开头
下面这段是文档的开头,其中定义了一些有用的命名空间,本文要用到的是:
ss:"urn:schemas-microsoft-com:office:spreadsheet",
它代表了工作表空间。
中间一些跳过不管,直接看Worksheet工作表段落。Worksheet字段的Name属性表示工作表名称 Table 字段中的保存了表格的行、列数目等属性 :
接下来是关键部分,也是最乱的地方,表的数据区。
上面的数据区如下图所示:|
|
a | b | ............
9 | 10 |
数据区在下面是其他Table表格、下一个Worksheet工作区、表格样式等信息,这里不详细讨论了。
二、Execl XML数据区的格式框架
首先了解一下构架,从上面的数据区编码可以看出,有如下规律:整个表以行为基础,标记是Row,行号最小的数据记录在最前面的Row中(但不一定是第一个!,随后会说到),其Index属性表示绝对行号 列是Row的子结点,标记是Cell(同时也表示具体数据元素),该行中列号最小的数据记录在该行最前面的Cell中,其Index属性表示绝对列号 Data标记是Cell的子结点,其属性记录数据的类型等信息,其值就是数据本身 XML中行号、列号的Index属性是该数据在表中的绝对位置;但没有此属性的行、列则用相对位置表示,需要自己解析计算出绝对位置 无论是行还是列,如果其绝对位置是1,那么都没有Index属性;如果不是1,则有Index属性 无论是行还是列,如果两行(列)是相邻的,那么都没有Index属性;如果不相邻,则有Index属性 行、列位置的表示方法 同时存在相对和绝对表示,Index属性的存在不可预知,在解析时一定要注意 Table使用的命名空间是"urn:schemas-microsoft-com:office:spreadsheet"
需要注意的是,根据存盘情况不同,Row的情况会有变化,目前观察有如下几种:
用Execl第二次保存之前:不会自动出现AutoFitHeight属性;绝对行号是1则没有Index属性且是第一个Row;绝对行号是2(且是行号最小的数据)则有Index属性(=2)且是第一个Row 用Execl第二次保存之后:所有Row均自动加上AutoFitHeight属性;绝对行号是1则没有Index属性且是第一个Row;绝对行号是2(且是行号最小的数据)则没有Index属性且是第二个Row 下载本文示例代码
由于Row的框架会变,我们的程序就必须覆盖所有情况。不知道MS为什么要这样设计,但是很明显我们写代码时将会比较痛苦......三、部分解析代码
全部的Execl单元格解析比较复杂,这里只介绍基本的XML解析。
.net XML 解析模型
1.读入Execl XML文件
//有时候用户需要打开Execl的同时做解析,所以我们使用流来构造XML //我们的目的只是读出单元格,所以使用XPathDocument可以提高解析性能 //用到的命名空间 using namespace System::IO; using namespace System::Xml; using namespace System::Xml::XPath; //按下面的参数构造流 FileStream^ sr = File::Open("c:\Book1.XML",FileMode::Open,FileAccess::Read,FileShare::ReadWrite); //用流来构造XPathDocument XPathDocument^ doc = gcnew XPathDocument(sr);2.构造游标、命名空间//用XPathDocument来构造XPathNavigator游标 XPathNavigator^ navigator = doc->CreateNavigator(); //navigator->NameTable是被原子化的XML文档内容 XmlNamespaceManager^ manager = gcnew XmlNamespaceManager(navigator->NameTable); //添加命名空间,并起个名字叫ss manager->AddNamespace("ss","urn:schemas-microsoft-com:office:spreadsheet");3.查询节点//找到指定WorkSheet中Name属性,Select需要节点的绝对路径 XPathNodeIterator^ node = navigator->Select("/ss:Workbook/ss:Worksheet/@ss:Name",manager); XPathNavigator^ child = node->Current; //未知原因SelectChild()查询子结点总是失败4.操作节点游标//移动到节点的第一个属性 child->MoveToFirstAttribute(); //移动到该节点的下一个属性 child->MoveToNextAttribute(); //如果在属性中则MoveToParent()回到节点,如果在节点上则回到上一级节点 child->MoveToParent(); //移动到下一个同级结点 child->MoveToNext(); //移动到第一个子结点 child->MoveToFirstChild(); //还有其他很多 ..... //得到当前游标位置上的名称(可能是节点名称(标记),也可能是属性名称) child->Name; //得到当前游标位置上的值(可能是节点的值,也可能是属性值) chile->Value;四、示例代码
示例代码的功能是取出Execl中指定的一列,并按照这列的值建立同名的文件夹。
由于Execl XML的特殊结构,程序里面的解析比较复杂,大家下载后有时间可以看看。
程序现在还有些小问题,需要改进。
解析思路为:
1.找到用户要求的Worksheet工作表
2.游标移动到Table
3.找到第一个带数据的Row;如果是相对行位置,则计算出其绝对行号(只有可能是1或2)
4.按照第一个带数据的Row所在的绝对行号,找到用户要求的起始行所在的Row位置;注意其间会混杂相对行位置和绝对行位置
5.在该Row内找用户要求的列(注意其间会混杂相对列位置和绝对列位置);如果找不到,则下移一行(注意其间会混杂相对行位置和绝对行位置),再找,直到找到该列为止
6.得到Data的值(取得指定数据项),做操作
7.如果用户指定的结束行号大于表中的行号,则以表中的末尾行号为准
运行效果:
数据解析工作非常辛苦、烦闷,同时也需要极大的耐心。希望我的这篇文章能给大家提供一些帮助。
阅读(691) | 评论(0) | 转发(0) |给主人留下些什么吧!~~