Pugixml一种快速解析XML文件的开源解析库
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文件的开源解析库的更多相关文章
- java解析XML文件四种方法之引入源文件
1.DOM解析(官方) try { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); Documen ...
- JAVA使用SAX解析XML文件
在我的另一篇文章(http://www.cnblogs.com/anivia/p/5849712.html)中,通过一个例子介绍了使用DOM来解析XML文件,那么本篇文章通过相同的XML文件介绍如何使 ...
- 深入浅出如何解析xml文件---上篇
xml小伙伴们并不陌生,xml是可扩展标记语言,标准通用标记语言语言的子集,是一种用来标记电子文件使其具有结构性的标记语言.我们知道xml可以用dom与sax等方法进行解析,但是xml为什么要解析呢? ...
- python 解析 XML文件
如下使用xml.etree.ElementTree模块来解析XML文件.ElementTree模块中提供了两个类用来完成这个目的: ElementTree表示整个XML文件(一个树形结构) Eleme ...
- Android -- 创建XML文件对象及其序列化, pull解析XML文件
1. 创建XML文件对象及其序列化 示例代码:(模拟以xml格式备份短信到SD卡) SmsInfo.java, bean对象 /** * 短信的业务bean * @author Administrat ...
- 解析xml文件的几种技术与Dom4j与sax之间的对比
一.解析xml文件的几种技术:dom4j.sax.jaxb.jdom.dom 1.dom4j dom4j是一个Java的XML API,类似于jdom,用来读写XML文件的.dom4j是一个非常优秀的 ...
- 解析XML文件的几种常见操作方法—DOM/SAX/DOM4j
解析XML文件的几种常见操作方法—DOM/SAX/DOM4j 一直想学点什么东西,有些浮躁,努力使自己静下心来看点东西,哪怕是回顾一下知识.看到了xml解析,目前我还没用到过.但多了解一下,加深点记忆 ...
- 解析xml文件的四种方式
什么是 XML? XML 指可扩展标记语言(EXtensible Markup Language) XML 是一种标记语言,很类似 HTML XML 的设计宗旨是传输数据,而非显示数据 XML 标签没 ...
- 解析XML文件的几种方式及其比较
解析xml文件目前比较流行的主要有四种方式: 1. DOM(Document Object Model)它把整个XML文档当成一个对象加载到内 存,不管文档有多大.它一般处理小文件 2.SAX(Si ...
随机推荐
- JestClient
JestService.java [html] view plain copy 在CODE上查看代码片派生到我的代码片 public class JestService { /** * 获取JestC ...
- 构建自己的PHP框架之自动加载类中详解spl_autoload_register()函数
在了解这个函数之前先来看另一个函数:__autoload. 一.__autoload 这是一个自动加载函数,在PHP5中,当我们实例化一个未定义的类时,就会触发此函数.看下面例子: printit.c ...
- js正则表达式的一些研究,截取两个字符串中间的字符串
一个最常用的场景 截取两个字符串中间的字符串 var str = "iid0000ffr"; var substr = str.match(/id(\S*)ff/); ...
- BAYSY2 的LVDS引脚 笔记
差分引脚标号说明: 'L' 代表该引脚属于差分引脚 'xx' 两位整型数,在每一 bank 的独特标记 'y' 表示正向 还是 反向,同时要注意输入输出方向 ‘#’ 0~3,代表 bank0~bank ...
- 2014年---移动端webapp个人年度总结
我今年是由零基础开始入门的,刚好我第一家公司入职后就马上让我接手做ipad版的专题app了.(一入门就是移动端开发,是幸运也是艰辛的开始). 我是自学前端的,当然,对Bootstrap,JQuery ...
- JavaScript - 原型
一切皆为对象 殊不知,JavaScript的世界中的对象,追根溯源来自于一个 null 「一切皆为对象」,这句着实是一手好营销,易记,易上口,印象深刻. 万物初生时,一个null对象,凭空而生,接着O ...
- c#生成二维码
String link ="www.baidu.com";//这里一般是一个链接 封装后的方法,直接调用就可以了 public void CreateQRCode ...
- 如何删除已安装的Windows服务
1) 开始-运行,输入regedit命令. (Windows键+R,输入regdeit) 2) 回车后会弹出一个窗口:注册编辑器.找到 HKEY_LOCAL_MACHINE\SYSTEM\Curre ...
- js传入参数为字符串问题
示例: var device_mac="11qweq234ert"; //第一种方式会报错:Onclick SyntaxError: identifier starts immed ...
- 常用ubuntu命令
解压缩.7z sudo apt-get install p7zip-full 7z x PACKAGE.7z 查看图片 eog A.png 关闭打开触摸板(触点) sudo rmmod psmouse ...