Pugixml是一个轻量级的C++ XML开源解析库,DOM形式的解析器、接口和丰富的遍历和修改操作,快速的解析,此外支持XPath1.0实现数据查询,支持unicode编码;

  使用Pugixml可通过直接在项目中包含其几个文件或者编译为动态库dll、静态库lib的形式供其他项目使用、比较方便,如果需要推荐编译为静态库或文件包含即可;

  Pugixml项目中提供了文档手册、快速使用指南,可参考文档说明和smaples中的示例代码尝试快速上手使用,以及源码分析;

  搭建好环境、工程(具体可参照文档、手册),我们以smaples中的load_file.cpp文件作为分析的出发点,运行程序,观察执行结果,当然需要当前路径下的tree.xml文件,否则会加载失败;可以预先查看tree.xml文件内容,该文件作为经典例子来学习,内容基本涵盖了整个解析器可实现的解析功能,继续调试跟踪;

  首先pugi::xml_document作为文档类也作为DOM树的根节点类,其继承于xml_node节点类;在Pugixml中xml_node节点类作为操作节点的轻量级基础类,基本上大多数操作基于此类;xml_node节点类实现的操作接口比较多,但是成员变量仅有一个_root,该变量类型为节点结构,作为当前节点的根;继续跟踪节点结构定义;

xml_node_struct:节点结构

header:目前还不知道含义,根据初始化可推测为指向分配的内存页首地址;

name:节点名称;

value:节点的值;

parent:父节点;

first_child:第一个子节点;

prev_sibling_c:上一个兄弟节点;

next_sibling:下一个兄弟节点;

first_attribute:节点的第一个属性;

xml_attribute_struct:节点的属性结构

header:指向内存地址首地址;

name:节点属性名称;

value:节点属性的值;

prev_attribute_c:上一个兄弟属性;

next_attribute:下一个兄弟属性;

事实上xml_node节点类的成员_root标识当前节点的(作为当前节点的根节点),所以xml_document的_root则为整个DOM树的根节点;xml_node节点类的其他成员暂不分析后面会分析到;

xml_memory_page:内存页

allocator:内存分配器对象;

prev:上一个内存页;

next:下一个内存页;

busy_size:正使用的内存页大小;

freed_size:空闲的内存页大小;

xml_allocator:内存分配器(提供了分配和释放内存的操作接口)

_root:内存页根节点;

_busy_size:已使用内存大小;

xml_document_struct:文档结构类(继承于xml_node_struct、xml_allocator)

buffer:文档结构缓冲区;

extra_buffers:额外的缓冲区;

  

xml_extra_buffer:额外缓冲区

buffer:缓冲区;

next:下一个额外缓冲区;

xml_document类,可以发现继承了xml_node类操作还增加了一些加载和保存相关的操作接口,以及create、destory、reset,这几个函数结合_buffer、_memory主要用来预分配、初始化页内存分配、对齐或释放操作;

create: 内部操作;

1.检验哨兵页_memory是否够用以保证分配页起始位置仍在_memory范围内;

2.对齐分配页起始内存位置(按照xml_memory_page_alignment长度对齐);

3.将在_memory中的得到的起始内存页位置初始化得到内存页page;

4.将page的正使用的内存页大小busy_size设置为xml_memory_page_size(32768个字节);

5.在_memory的page页结构后new重分配xml_document_struct大小的空间作为_root根节点,并将_root的上一个兄弟节点指向自身,_root节点作为page的内存分配器;

6.再次检验page后重分配xml_document_struct大小的空间是否超过_memory范围;

destroy:内部操作

释放_buffer缓冲空间、_root下的额外缓冲空间以及_root的兄弟缓冲空间;

reset:内部调用create、destroy,另外一个重载版本支持拷贝另一个xml_document来重新初始化DOM树;

基本上我们已确定的当前内存布局方式:

以_memory作为基础,在_memory上建立page(root_page)、_root布局和位置定位,确定page与_root间的关系,此外_root扮演着内存分配器的功能负责分配额外缓冲区xml_extra_buffer以及分配页缓冲区xml_memory_page;而xml_exrta_buffer维护一个额外缓冲区链表xml_extra_buffer交由_root管理, xml_memory_page维护自己的页缓冲区链表交由page管理,不过xml_document只保存了_root,但_root

成员已保存了page的首地址,可以追寻到page;总结:目前已经存在上述的两套链表;

文件加载和解析:

load或load_XXX:加载XML文件或文件内容,先暂时直接分析load_file接口,load_file()-->impl::load_file_impl()-->load_buffer_impl()-->impl::xml_parser::parse();以下将依次按照函数调用顺序进行分析:load_file:提供重载版本,参数path为xml文件路径,options为解析选项,默认解析模式为parse_default,即在DOM树种元素、PCDATA、CDATA块被扩展,结束换行符标准化、属性值按照CDATA块方式进行标准化处理;若选项为parse_full,则解析所有包含parse_default以及pi数据、注释数据、声明数据等;参数encoding为编码方式,默认为自动识别,pugixml提供了可支持的多种编码方式xml_encoding如:UTF8、Little-endian UTF16、Big-endian UTF16、Little-endian UTF32、Big-endian UTF32等;impl::load_file_impl:加载文件实现接口,增加参数_root,文件描述符,_buffer保存文件内容缓冲区;函数内部通过get_file_size获取文件大小并通过impl::xml_memory::allocate分配足够容纳所有文件内容的缓冲区(该分配器内部默认调用malloc和free,用户可通过set_memory_management_functions修改其为自己的内存分配器),通过get_buffer_encoding获取缓冲区内容真实的编码方式,最后通过调用zero_terminate_buffer修正buffer结束终止符号;load_buffer_impl:内部调用impl::convert_buffer实现编码格式的转化,具体的转化过程暂时跳过,不作分析(不过内部重新申请了一片新的转化后的缓冲区内容,并将早期的_buffer通过impl::xml_memory::deallocate释放掉了),_buffer指向了转化后的文件内容缓冲区,此外doc->buffer亦保存该新的缓冲区地址;impl::xml_parser::parse:解析文件内容缓冲区,内部通过xml_parser解析器调用parse_tree解析文件内容,完成DOM树构建(事实上每个节点都一个_root成员表示以自己为根节点时可以遍历其兄弟节点和子节点,故xml_xml_document的_root作为根节点可以遍历整个树的信息() ),此外每个节点或属性均通过内存分配器在堆上分配的(不过不用担心这些节点已在xml_memory_page和xml_extra_buffer中管理和分配,其已尽可能减少内存分配和内存碎片),最后说明所有节点的前一个兄弟节点若不存在时则指向自己,下一个兄弟节点不存在时指向空,所有的属性的前一个兄弟属性若不存在时也指向自己,下一个兄弟属性不存在时指向空;具体的解析过程不再去分析,比较繁琐,有时间可以去细化深入分析;

再次分析load/load_xxx加载接口,目前load接口提供了三个重载版本,分别支持std::basic_istream流、XML文件内容格式的字符串;无论哪种最终转化为调用接口load_buffer_impl()实现加载解析;load_string、load_buffer支持加载其他内存缓冲区或xml文件内容字符串;此外还有load_buffer_inplace、load_buffer_inplace_own,前者需要用户提供内容缓冲区且保证整个DOM树生存期内仍然存活,与其他加载方式不同,此接口下DOM内部不再拷贝副本;load_buffer_inplace_own也由用户提供内容缓冲区,但是DOM会接管该缓冲区,外部不再允许释放(!DOM会释放,意味着该缓冲区必须在堆上创建,建议不使用该接口);

文件保存:

save/save_file:保存XML内容至数据流或文件中;现在暂时分析save_file接口;save_file()-->impl::save_file_impl()-->save()-->impl::node_output();

以下将依次按照函数调用顺序进行分析:

save_file:参数path_为保存xml文件路径名,indent为缩排字符串,默认为”\t”,flags为输出格式选项,默认值format_default(节点缩进依赖于其在DOM树的深度,以及一个默认的声明),参数encoding为输出文件的编码方式,默认为自动encoding_auto(即默认的编码);impl::save_file_impl:保存文件实现,增加参数文档对象doc,文件描述符,内部通过创建一个xml_writer_file对象,并将该对象传入文档对象doc的save方法

实现文件保存;

save:保存文件操作,内部通过传入的xml_writer_file对象创建impl::xml_buffered_writer实例,该实例对象进行真正的写文件操作对象(内含一成员buffer当保存大于该容量阈值才进行文件flush,以减少写文件的次数),默认将声明写入文件,此后调用impl::node_output遍历DOM树节点并按照缩进格式和节点的当前树深度写入指定文件;

再次分析save接口,重载三个版本,分别支持输出到xml_writer、

std::basic_ostream,用户可以继承并重写xml_writer的write函数增加自己的操作,也可以输出到ostream流中;

节点添加、修改、删除:

pugixml提供了丰富的操作接口,具体可查看xml_node节点类相关接口,此外最重要的是内存布局、分配的问题,所有节点均在分配页中进行管理,基本上不用单独申请或释放任何节点,释放时调用destroy时会释放掉所有的那些节点或属性依附的分配页即可;

节点的遍历和查找:

pugixml提供了基本的遍历方式即通过child、next_sibling、next_attribute等可以较为方便的实现节点或属性的遍历;也提供了xml_node_iterator、xml_attribute_iterator节点迭代器和属性迭代器(事实上是对基本遍历方式的简单封装);此外提供了谓语查找,用户可以提供自己的查找函数(只需要提供仿函数或函数对象即可)主要用在find_child、find_node、find_attribute这几个接口;xml_document类提供了一个接口traverse,其可支持用户自定义的遍历“步行者”,通过继承xml_tree_walker类并实现for_each回调函数接口,该接口将遍历所有节点,返回值为true则继续爬行,否则停止爬行,此外用户也可以实现其begin与end接口,这两个接口分别在for_each执行前后,可增加必要的初始化操作或收尾操作,返回false则退出;该类中成员_depth为当前节点的爬行深度,可通过depth()接口获取;

额外说明:

值得说明的是pugiconfig.hpp配置文件,里面定义了许多宏,如:

PUGIXML_WCHAR_MODE:开启unicode支持;

PUGIXML_COMPACT:支持紧凑型内存管理布局;

PUGIXML_NO_XPATH:不需要xpath支持,可减少pugixml编译为库的体积;

PUGIXML_NO_STL:不使用STL支持,对于不想使用STL环境下比较有用;

PUGIXML_NO_EXCEPTIONS:不允许抛出异常;

PUGIXML_API:编译导出为DLL;

PUGIXML_MEMORY_PAGE_SIZE:内存页大小;

PUGIXML_HAS_LONG_LONG:支持long long数据类型;

PUGIXML_HEADER_ONLY:只包含头文件,这样头文件内部会自己包含cpp文件;

  可根据需要使用或修改相应的宏;

总结:

  pugixml提供了丰富的节点操作和遍历接口并以DOM形式构建,此外内存管理提供用户内存分配器以及支持对齐或紧凑型内存布局、文件解析并支持多种编码方式xml文件和编码格式的相互转化,支unicode,默认为utf-8格式;(事实上xml文件加载或文件内存缓冲区加载的时候,可以提前释放xml_document类成员_buffer的空间,因后面一直没有用到,且释放空间可供进程重新申请使用该释放的空间);接口使用简便、操作解析也比较快速,内存管理相对tinyxml内存碎片也会少很多且指针访问比较集中(tinyxml内部无内存管理,可能也会导致内存碎片以及轻微的访问、操作较慢一些)。

Pugixml一种快速解析XML文件的开源解析库的更多相关文章

  1. java解析XML文件四种方法之引入源文件

    1.DOM解析(官方) try { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();         Documen ...

  2. JAVA使用SAX解析XML文件

    在我的另一篇文章(http://www.cnblogs.com/anivia/p/5849712.html)中,通过一个例子介绍了使用DOM来解析XML文件,那么本篇文章通过相同的XML文件介绍如何使 ...

  3. 深入浅出如何解析xml文件---上篇

    xml小伙伴们并不陌生,xml是可扩展标记语言,标准通用标记语言语言的子集,是一种用来标记电子文件使其具有结构性的标记语言.我们知道xml可以用dom与sax等方法进行解析,但是xml为什么要解析呢? ...

  4. python 解析 XML文件

    如下使用xml.etree.ElementTree模块来解析XML文件.ElementTree模块中提供了两个类用来完成这个目的: ElementTree表示整个XML文件(一个树形结构) Eleme ...

  5. Android -- 创建XML文件对象及其序列化, pull解析XML文件

    1. 创建XML文件对象及其序列化 示例代码:(模拟以xml格式备份短信到SD卡) SmsInfo.java, bean对象 /** * 短信的业务bean * @author Administrat ...

  6. 解析xml文件的几种技术与Dom4j与sax之间的对比

    一.解析xml文件的几种技术:dom4j.sax.jaxb.jdom.dom 1.dom4j dom4j是一个Java的XML API,类似于jdom,用来读写XML文件的.dom4j是一个非常优秀的 ...

  7. 解析XML文件的几种常见操作方法—DOM/SAX/DOM4j

    解析XML文件的几种常见操作方法—DOM/SAX/DOM4j 一直想学点什么东西,有些浮躁,努力使自己静下心来看点东西,哪怕是回顾一下知识.看到了xml解析,目前我还没用到过.但多了解一下,加深点记忆 ...

  8. 解析xml文件的四种方式

    什么是 XML? XML 指可扩展标记语言(EXtensible Markup Language) XML 是一种标记语言,很类似 HTML XML 的设计宗旨是传输数据,而非显示数据 XML 标签没 ...

  9. 解析XML文件的几种方式及其比较

    解析xml文件目前比较流行的主要有四种方式: 1. DOM(Document Object Model)它把整个XML文档当成一个对象加载到内  存,不管文档有多大.它一般处理小文件 2.SAX(Si ...

随机推荐

  1. spring mvc 的jpa JpaRepository数据层 访问方式汇总

    本文转载至:http://perfy315.iteye.com/blog/1460226 AppleFramework在数据访问控制层采用了Spring Data作为这一层的解决方案,下面就对Spri ...

  2. RabbitMQ学习系列(六): RabbitMQ 高可用集群

    前面讲过一些RabbitMQ的安装和用法,也说了说RabbitMQ在一般的业务场景下如何使用.不知道的可以看我前面的博客,http://www.cnblogs.com/zhangweizhong/ca ...

  3. Mac下安装ionic和cordova,并生成iOS项目

    为了开发HTML5,除了最新使用React Native等之外,目前首选的为稳定的ionic+Angularjs来开发iOS和android. Ionic(ionicframework一款接近原生的H ...

  4. ump_player在线直播播放器

      ump_player在线直播播放器封装,可以支持加载在线直播流 https://github.com/Mrxdh/EveryExercise/blob/master/ump_player%E5%9 ...

  5. nGrinder安装指南

    NGrinder 由两个模块组成,其运行环境为 Oracle JDK 1.6 nGrinder controller  web 应用程序,部署在Tomcat 6.x 或更高的版本 nGrinder A ...

  6. JAVA语法基础作业——动手动脑以及课后实验性问题 (八)

    一.动手动脑 运行AboutException.java示例,了解Java中实现异常处理的基础知识. 1)源代码 import javax.swing.*; class AboutException ...

  7. Apache服务器安装过程及问题的解决(for windows system32bit)

    在使用Hbuilder设计网站时,在制作本站搜索时,用到了Php文件,而Hbuilder的内置web服务器不支持php的解析, 所以需要安装配置外部服务器,有多个选择,我安装的apache服务器,并遇 ...

  8. Json在PHP与JS之间传输

    1. JS-->PHP a). JS create Json <script> $(document).ready(function(){ /*--JS create Json--* ...

  9. V4L2框架分析学习二

    转载于:http://www.techbulo.com/1198.html v4l2_device v4l2_device在v4l2框架中充当所有v4l2_subdev的父设备,管理着注册在其下的子设 ...

  10. ubuntu14.04下搭建python+mysql环境

    简略记录ubuntu14.04下搭建python操作的mysql服务器的过程和其中遇到的问题及解决方法. 第一部分: 安装mysql 安装步骤:1. sudo apt-get install mysq ...