利用 .Net XML 解析 Execl XML单元格

/Files/chaobaojun/parseexcelxmlsrc.zip  下载源代码 转载 http://www.vckbase.com/document/viewdoc/?id=1726

阅读本文需要有 XML 规范基础 及 .net XML 解析基础

本文示例代码使用 Visual Stdio 2005 、Office Execl 2007

一、首先我们来看看Execl XML的开头

下面这段是文档的开头,其中定义了一些有用的命名空间,本文要用到的是:

ss:"urn:schemas-microsoft-com:office:spreadsheet",

它代表了工作表空间。

    <?xml version="1.0" ?> 
    <?mso-application progid="Excel.Sheet"?>
    <workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
    xmlns:o="urn:schemas-microsoft-com:office:office"
    xmlns:x="urn:schemas-microsoft-com:office:excel"
    xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
    xmlns:html="http://www.w3.org/TR/REC-html40">
    </workbook>  
  中间一些跳过不管,直接看Worksheet工作表段落。Worksheet字段的Name属性表示工作表名称 Table 字段中的保存了表格的行、列数目等属性 :
      <Worksheet ss:Name="Sheet1">
      <Table ss:ExpandedColumnCount="7" ss:ExpandedRowCount="13" x:FullColumns="1"
      x:FullRows="1" ss:DefaultColumnWidth="54" ss:DefaultRowHeight="13.5">
      
  接下来是关键部分,也是最乱的地方,表的数据区。
      <Row ss:AutoFitHeight="0"/>
      <Row ss:AutoFitHeight="0"/>
      <Cell ss:Index="2"><Data ss:Type="String">a</Data></Cell>
      <Cell><Data ss:Type="String">b</Data></Cell>
      ......
      </Row>
      <Row ss:Index="4" ss:AutoFitHeight="0">
      <Cell><Data ss:Type="String">9</Data></Cell>
      <Cell><Data ss:Type="String">10</Data></Cell>
      </Row>
      ......
  上面的数据区如下图所示:

  

  数据区在下面是其他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.如果用户指定的结束行号大于表中的行号,则以表中的末尾行号为准
      
    运行效果:

    数据解析工作非常辛苦、烦闷,同时也需要极大的耐心。希望我的这篇文章能给大家提供一些帮助。

  • posted @ 2009-12-18 12:47  chaobj  阅读(722)  评论(0编辑  收藏  举报