热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

别人写了一个mircroXML解析器,附源代码

mirco的意思是比tiny还要tiny。GUI模板用XML做是最合适的。方便嵌入脚本,方便编辑修改,方便嵌入皮肤描述,用XML做模板,写起GUI编辑器也要方
mirco 的意思是比 tiny 还要 tiny。

  GUI 模板用 XML 做是最合适的。方便嵌入脚本,方便编辑修改,方便嵌入皮肤描述,用 XML 做模板,写起 GUI 编辑器也要方便得多。

  以前几个的 GUI 模板解析器用的是 MSXML 来实现的,不过它提供的接口字符串类型全是 BSTR,自己的接口又是 TCHAR*,每一次的调用都会有一次字符串转换,效率很低。而且一想到代码里有 QueryInteface,AddRef,Release ,心里就不踏实,担心吊胆的。

  于是又打算用 TinyXML,看了几遍代码,发现它对宽字符集的支持有些古怪。代码里其它的杂七杂八的东西有点多,代码风格什么的也不大喜欢。

  于是自己写了一个简单的,虽然有错误检测机制,不过没有提供查询接口,因为这样我觉得使接口不整洁。其实要知道 XML 哪里出错了很简单,随便拿个浏览器打开就行了,提示得非常详细。不过以后可能会根据需要添加上。XPATH 支持也是如此。接口函数全部使用 UNICODE。

 

测试代码:

   1: // cexer
   2: #include "./include/XML/xmlfile.h"
   3: #include "./include/XML/xmlelement.h"
   4: #include "./include/XML/xmldeclaration.h"
   5: #include "./include/XML/xmlunknown.h"
   6: #include "./include/XML/xmlcomment.h"
   7: #include "./include/XML/xmltext.h"
   8: #include "./include/XML/xmlattribute.h"
   9:  
  10: using namespace cexer;
  11: using namespace cexer::xml;
  12:  
  13:  
  14: // c++ std
  15: #include 
  16: using namespace std;
  17:  
  18:  
  19: int wmain( int argc,WCHAR** argv )
  20: {
  21:     wcout.imbue( std::locale("chs") );
  22:  
  23:     XmlFile document( L"./XMLs/utf16le_ns.xml" );
  24:     if ( !document.load() )
  25:     {
  26:         wcout<"解析失败"< 
   
  27:         return 0;
  28:     }
  29:  
  30:     XmlElement* root = document.element();
  31:     if ( !root )
  32:     {
  33:         wcout<"没有找到根结点"< 
   
  34:         return 0;
  35:     }
  36:  
  37:     wcout<name()< 
   
  38:  
  39:  
  40:     XmlNode* firstChild = root->firstChild();
  41:     if ( !firstChild )
  42:     {
  43:         wcout<"没有子结点"< 
   
  44:         return 0;
  45:     }
  46:  
  47:     XmlDeclaration* declar = xml_cast( firstChild );
  48:     if ( !declar )
  49:     {
  50:         wcout<"第一个子结点不是声明"< 
   
  51:     }
  52:     else
  53:     {
  54:         wcout<"第一个节点是声明"< 
   
  55:         wcout<"version = "<version()< 
   
  56:         wcout<"encoding = "<encoding()< 
   
  57:         wcout<"standalOne= "<standalone()< 
   
  58:     }
  59:  
  60:     XmlComment* comment = xml_cast( firstChild->next() );
  61:     if ( !comment )
  62:     {
  63:         wcout<"第二个子结点不是注释"< 
   
  64:     }
  65:     else
  66:     {
  67:         wcout<"第二个结点是注释:"<value()< 
   
  68:     }
  69:  
  70:     XmlElement* window = root->element( L"window" );
  71:     if ( !window )
  72:     {
  73:         wcout<"没有找到window元素结点"< 
   
  74:         return 0;
  75:     }
  76:     wcout<attributeValue( L"text" )< 
   
  77:  
  78:  
  79:     XmlElement* nextWindow = window->nextElement( L"window" );
  80:     if ( !nextWindow )
  81:     {
  82:         wcout<"没有找到后面的window元素结点"< 
   
  83:     }
  84:     else
  85:     {
  86:         wcout<"后面还有一个window元素结点 ";
  87:         wcout<attributeValue(_T("name"))< 
   
  88:     }
  89:  
  90:     XmlElement* panel = window->element( L"panel" );
  91:     if ( panel )
  92:     {
  93:         wcout<attributeValue( L"caption" )< 
   
  94:     }
  95:  
  96:     window->setAttribute( L"styleAdd",L"WS_VISIBLE" );
  97:     window->setAttribute( L"styleRemove",L"\";<>=&\"" );
  98:  
  99:     if ( !document.save( L"./XMLs/modified/utf16le_ns.xml" ) )
 100:     {    
 101:         wcout<"保存失败"< 
   
 102:     }
 103:  
 104:     return 0;
 105: }

 

编码支持:

  因为内部使用 MultiBytesToWideChar 和 WideCharToMultiBytes 来实现字符集/编码的操作,因此对字符集/编码的支持很灵活,能够支持以上两个函数支持的所有编码,只需简单的修改即可添加新的支持。预置了几种编码支持:

  1. UTF-16LE(UNICODE)    
  2. GBK    
  3. BIG5    
  4. GB2312    
  5. UTF-7    
  6. UTF-8

  对于没有编码声明并且没有文件头签名文件,都视为 UTF-8 编码。以上的代码当中的 XML 文件就是一个没有签名,没有声明的 UTF-16LE 编码的 XML 文件。

  TinyXML 内部解析字符串全是以 char*  类型来解析,只支持多字节编码如 UTF-8,ASCII,不支持 UNICODE 编码。如对中文的支持就比较古怪,如果 XML 以 UTF-8 格式保存,则设置“值”的时候必须自己把字符串转换为 UTF-8 再设置,而在取得值以后,则必须自己将它们从 UTF-8 转换成 UNICODE 或 ASCII,否则就是乱码。

  主要是 TinyXML 为了跨平台,所以没有像 MultiBytesToWideChar  和 WideCharToMultiBytes  这种函数的直接支持。不过如果是为了跨平台,这两个函数也可以考虑自己实现。

 

节点类型转换:

  因为在内存当中,元素,注释,文本,声明等结点都是存储为一个基类的指针,因此在使用的时候必须要有一个类型转换的动作。也有一些 XML 解析器比较简单,其中只有文档,元素,属性这三种结点,内存当中存的全部都是元素集合,元素当中再存有属性集合,用不着类型转换,不过这种 XML 将 XML 读入内存再存入磁盘文件当中时,会丢失掉 XML 文件原有的格式。

  TinyXML 在内存当中所有的结点(除属性)都以基类 TiXmlNode 指针的形式存放 ,从一个 TiXmlNode* 进行类型转的方法是这样的:

  首先在基 TiXmlBase 声明一组虚函数

   1: class TiXmlNode
   2: {
   3:     virtual TiXmlDocument*   ToDocument()    { return 0; }
   4:     virtual TiXmlElement*    ToElement()        { return 0; }
   5:     virtual TiXmlComment*    ToComment()     { return 0; }
   6:     virtual TiXmlUnknown*    ToUnknown()        { return 0; }
   7:     virtual TiXmlText*       ToText()        { return 0; }
   8:     virtual TiXmlDeclaration*    ToDeclaration() { return 0; }
   9: };

 
  然后在子类当中各自重写向自己类型转换的那一个虚函数。如在 XmlElement 和 XmlComment 当中:

   1: class XmlElement
   2: {
   3:     virtual TiXmlElement*    ToElement()        { return this; }
   4: };
   5:  
   6: class XmlComment
   7: {
   8:     virtual TiXmlComment*    ToComment()     { return this; }
   9: };


  这样做很方便,不过如果要写一个新的 XML 结点类型,就比较麻烦了,必须重新修改基类 TiXmlNode 的代码,整个 XML 解析器的代码都得重新编译一遍。

  我则借用了 COM 的 Queryinterface 的方式。在基类 XmlCastable 当中声明了一个虚函数 query,任意结点调用这个函数,输入一个类型,若该结点是这个类型,那么就输出指针的值并返回 true,否则输出 NULL 并返回 false。

   1: class XmlCastable
   2: {
   3:     virtual bool query( XmlType destType,void** ppvoid ) = 0;
   4: };
   5:  


  如在 XmlNamedNode  当中:

   1: bool XmlNamedNode::query( XmlType type,void** pptr )
   2: {
   3:     if ( !pptr )
   4:     {
   5:         return false;
   6:     }
   7:  
   8:     *pptr = NULL;
   9:  
  10:     if ( XmlNamedNode::s_type == type )
  11:     {
  12:         *pptr = this;
  13:         return true;
  14:     }
  15:  
  16:     return false;
  17: }


  不过这样使用起来会相当地麻烦,所以写了一个辅助函数 xml_cast,可以对 XmlNode* 这样调用:

   1: XmlNode* node = ...;
   2: XmlElement* element = xml_cast( node );
   3: XmlComment* comment = xml_cast( node );


类结构:

   1: // 给函数 xml_cast 提供结点类型的转换能力
   2: class XmlCastable
   3: {...};
   4:  
   5: // 除了属性结点(XmlAttribute)外,所有结点类型的基础
   6: class XmlNode:public XmlCastable
   7: {...};
   8:  
   9: // 含有子结点的结点如:元素(XmlElement),文档(XmlDocument)
  10: class XmlParentNode:public XmlNode
  11: {...};
  12:  
  13: // 有名字的结点如:元素(XmlElement),属性(XmlAttribute)
  14: class XmlNamedNode:public XmlCastable
  15: {...};
  16:  
  17: // 有值的结点如:属性(XmlAttribute),文本(XmlText),注释(XmlComment),未知(XmlUnknown)
  18: class XmlValueNode:public XmlCastable
  19: {...};
  20:  
  21:  
  22: // XML声明 
  23: class XmlDeclaration:public XmlNode
  24: {...};
  25:  
  26:  
  27: // XML注释结点
  28: class XmlComment:public XmlNode
  29:                  ,public XmlValueNode
  30: {...};
  31:  
  32:  
  33: // XML文本结点 [文本] 及 
  34: class XmlText:public XmlNode
  35:               ,public XmlValueNode
  36: {...};
  37:  
  38: // XML文档结点
  39: class XmlDocument:public XmlParentNode
  40: {...};
  41:  
  42: // XML元素结点
  43: class XmlElement:public XmlParentNode
  44:                  ,public XmlNamedNode
  45: {...};
  46:  
  47: // XML元素属性
  48: class XmlAttribute:public XmlNamedNode
  49:                    ,public XmlValueNode
  50: {...};
  51:  
  52: // 解析器暂不支持的XML结点如
  53: //    
  54: //    
  55: //    
  56: class XmlUnknown:public XmlNode
  57:                  ,public XmlValueNode
  58: {...};
  59:  
  60:  


附件下载:

  从 cexer 的库当中把 xml 解析器剥离出来放上来。希望大家也都积极踊跃地共享代码。(注:还没有怎么详细测试,切不可用于重要的项目如国防工程火箭发射工程之类的,崩溃死机核弹爆炸什么的本人概不负责。。。。)

点击下载


推荐阅读
  • 本文介绍了一种划分和计数油田地块的方法。根据给定的条件,通过遍历和DFS算法,将符合条件的地块标记为不符合条件的地块,并进行计数。同时,还介绍了如何判断点是否在给定范围内的方法。 ... [详细]
  • c语言\n不换行,c语言printf不换行
    本文目录一览:1、C语言不换行输入2、c语言的 ... [详细]
  • 本文介绍了解决二叉树层序创建问题的方法。通过使用队列结构体和二叉树结构体,实现了入队和出队操作,并提供了判断队列是否为空的函数。详细介绍了解决该问题的步骤和流程。 ... [详细]
  • 本文介绍了C函数ispunct()的用法及示例代码。ispunct()函数用于检查传递的字符是否是标点符号,如果是标点符号则返回非零值,否则返回零。示例代码演示了如何使用ispunct()函数来判断字符是否为标点符号。 ... [详细]
  • Linux环境变量函数getenv、putenv、setenv和unsetenv详解
    本文详细解释了Linux中的环境变量函数getenv、putenv、setenv和unsetenv的用法和功能。通过使用这些函数,可以获取、设置和删除环境变量的值。同时给出了相应的函数原型、参数说明和返回值。通过示例代码演示了如何使用getenv函数获取环境变量的值,并打印出来。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文介绍了为什么要使用多进程处理TCP服务端,多进程的好处包括可靠性高和处理大量数据时速度快。然而,多进程不能共享进程空间,因此有一些变量不能共享。文章还提供了使用多进程实现TCP服务端的代码,并对代码进行了详细注释。 ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 动态规划算法的基本步骤及最长递增子序列问题详解
    本文详细介绍了动态规划算法的基本步骤,包括划分阶段、选择状态、决策和状态转移方程,并以最长递增子序列问题为例进行了详细解析。动态规划算法的有效性依赖于问题本身所具有的最优子结构性质和子问题重叠性质。通过将子问题的解保存在一个表中,在以后尽可能多地利用这些子问题的解,从而提高算法的效率。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
author-avatar
哲亚Zoe
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有