[转]pugixml 一个xml解析库 简单快速, 支持XPath表达式, 感谢作者

原文链接http://blog.csdn.net/jdzfjfhnui/article/details/6672532只是简单的翻译了pugixml文档,暂时未翻译XPath部分./*1.对象模型pugixaml存储XML数据为DOM风格,整个xml文档(文档结构和元素数据)被存储在内存中作为一棵树.这棵树可以从字符流(文件,字符串,C++ I/O流)中加载,可以使用特定的API和XPath表达式
摘要由CSDN通过智能技术生成
原文链接http://blog.csdn.net/jdzfjfhnui/article/details/6672532

只是简单的翻译了pugixml文档,暂时未翻译XPath部分.


/*
1.对象模型

pugixaml存储XML数据为DOM风格,整个xml文档(文档结构和元素数据)被存储在内存中作为一棵树.这棵树可以从字符流(文件,字符串,C++ I/O流)中加载,可以使用
特定的API和XPath表达式来遍历它们.
整个树是可变的:节点结构和节点属性属性都可以在任何时候更改.
最后,可以将文档存储到字符流(文件, C++ I/O流,或自定的传输口).

XML文档由树结构呈现,树的根就是文档本身,由xml_document表示,
文档可以有一个或多个子节点, 由xml_node表示, 节点有不同的类型,视节点类型而定,一个节点可以有一组子节点,一组属性(由xml_attribute表示),
和额外的属性,例如name.

 

节点类型定义在枚举xml_node_type

1.文档节点(node_document)
  这是树的根,它由一些子节点组成.这个节点相当于xml_document类,xml_document是xml_node的子类,因此节点所有的接口都可以用,
  然而文档节点是有专门用途的,稍后解释.一棵树中只能有一个文档接口,  document node does not have any XML representation

2.元素/标记 节点(node_element)
  这是最普遍的节点类型,它代表XML元素.元素节点可以有一个名字,一组属性, 一组子节点(属性和子节点都可以为空).属性就是一组简单name/value pair.
  就像如下:
  <node attr="value"><child/></node>
   
 这里有两个元素节点,一个名字为node, 有一个属性attr, 和一个子节点, 另一个元素的名字为chid,它没有任何属性和子节点.

3.文本字符数据节点(node_pcdata)
 
 PCDATA 有一个值,但没有名字或子节点/或属性.  这种节点只能作为PCDATA插入到其他节点.例如一个元素可以有几个子PCDATA节点,下面是一个例子:
 <node> text1 <child/> text2 </node>

 这里 node元素有三个子节点, 两个为PCDATA节点,分别为text1和text2.还有一个child节点.


4.字符数据节点(node_cdata)
  在XML用文本表示,他可以用特殊方式来引用.CDATA节点与PCDATA节点的不同之处是,CDATA在xml中表示.
  the above text example looks like this with CDATA: 
 <node> <![CDATA[[text1]]> <child/> <![CDATA[[text2]]> </node>
  
 CDATA节点可以让你容易的包含<, & and > characters in plain text. 
 CDATA的值不可以包含字符序列]]>, since it is used to determine the end of node contents.

 
5.注释节点(node_comment)
  在XML中表示注释.注释节点可以有值,但不能有名字或子节点或属性.在XML中表示如下:

 <!-- comment text -->
 这个注释节点的值为comment text

 默认情况下注释节点不被视作为xml标记的一部分,因此在载入后分析器将丢弃它.你可以改变这个行为,通过加上parse_comments标记.


 6.处理指令节点(node_pi)
   在xml中表示processing instructions (PI).PI节点有一个名字和可选的值,但不能有子节点/属性.
   在XML中PI表现如下:
   
   <?name value?>
   name(可以称作PI target)为name, and the value is "value". 
   
   默认情况下PI节点不是被视作为xml标记的一部分,因此在载入后分析器将丢弃它.你可以改变这个行为,通过加上parse_pi标记.


  
  7.声明节点(node_declaration)
    在XML中表示文档声明.声明节点的名字为xml,和一组可选的属性, 它不能有值和子节点.
 在一个文档中只能有一个声明节点,而且,它只能在最文档的最前面.
   There can be only one declaration node in a document; moreover, it should be the topmost node (its parent should be the document).
   声明节点在XML表现如下:
   <?xml version="1.0"?>

   默认情况下声明节点不是视作为xml标记的一部分,因此在载入后分析器将丢弃它.你可以改变这个行为,通过加上parse_declaration标记.
   在保存xml文档的时候,如果文档有声明则使用该声明否则,使用库自己设置的声明节点.你可以禁止此功能,通过打上format_no_declaration标记.


   8.文档类型声明节点(node_doctype)
   在XML中表示文档类型声明.文档类型声明节点可以有一个值,它对于到整个文档类型内容;
 Document type declaration nodes have a value, which corresponds to the entire document type contents;
 no additional nodes are created for inner elements like <!ENTITY>. There can be only one document type declaration node in a document;
 moreover, it should be the topmost node (its parent should be the document). The example XML representation of a document type declaration node is as follows:
   <!DOCTYPE greeting [ <!ELEMENT greeting (#PCDATA)> ]>

   Here the node has value "greeting [ <!ELEMENT greeting (#PCDATA)> ]". By default document type declaration nodes are treated as
   non-essential part of XML markup and are not loaded during XML parsing. You can override this behavior with parse_doctype flag.

 

   所有的类和函数都在 pugi 名称空间.

   xml_document是不可拷贝的类,他包含加载和保存XML文档的接口和继承了xml_node的接口,用于对文档进行修改.
   注意,当xl_document为xml_node的子类型时,xml_node是一个多态类型,the inheritance is present only to simplify usage.
   另外你可以使用document_element函数去获取元素节点, that's the immediate child of the document.


   3.对象模型
 xml_document的默认构造函数初始化文档树为一个根节点(文档节点),你可以通过修改函数或加载函数来populate该树.所有加载函数将销毁先前树用的内存空间.
 并且使指向先前树的节点或者属性的句柄将变的无效.你可以使用xml_document::reset函数销毁树,它销毁树,你可以用一个空的或指定文档替换它.
 xml_document的析构函数将自动销毁文档树,因此文档对象的生命周期要超过任何节点/属性句柄的句柄生命周期.

  严格来说,节点/属性句柄可以继续生存即使他们引用的树已经销毁,对句柄调用任何成员函数将导致未知的行为.因此推荐只有在所有引用文档的节点/属性的句柄销毁后在销毁文档树.


  xml_node是文档节点的句柄;它可以指向文档中的任何节点,包括文档节点本身.它是所有节点类型的一个通用的接口;节点的实际类型可以通过xml_node::type来查询.
  注意:xml_node只有一个句柄指向实际的节点,而不是节点自身-你可以有多个xml_node句柄指向同一个对象.销毁xml_node句柄不会销毁该节点,也不会从树中移除该节点.

  xml_node的大小就等于指针大小,因此它仅仅是一个指针的轻量级包装类;你可以安全的传递或返回xml_node对象以值类型方式,而不会有额外开销.

  xml_node类型有一个特殊的值,叫做null节点或空节点(这样的节点的值为node_null).这样的节点没有对应到文档中的任何节点,就好比喻空指针.
  然而,在空节点上所有的操作都有定义(有特定返回值).一般来说,操作空节点不会做任何事情,而返回值将是空节点或空属性或空字符串.(see documentation for specific functions for more detailed information).

  这中设计很有用,例如当chaining调用时;你可用如下方式以获取某个节点的外祖父:
  node.parent().parent();
  如果node为null节点或它没有parent,第一次调用parent()将返回null节点,第二次调用parent()调用也同样返回null节点,这样更容易处理错误.

 

 

  xml_attribute是指向XML属性的句柄;它与xml_node是一样的语义,例如,可以有多个xml_attribute句柄指向同一个对象,也可以是特殊的null属性指针.

  xml_node和xml_attribute的默认构造函数初始化他们为null对象.

  xml_node和xml_attribute的行为都像指针,就是说,他们可以与同一类型的其他对象进行比较,以确保他们在同一个容器内是一致的.

  所有句柄如果指向同一个对象,则他们是相等的,否则不相等.null句柄只与自身比较时才相等.

  The result of relational comparison can not be reliably determined from the order of nodes in file or in any other way. Do not use relational comparison operators except for
  search optimization (i.e. associative container keys).

  如果你想使用xml_node或xml_attribute对象作为哈希表的键,你可以使用has_value成员函数来获取该对象的哈希值.null指针的哈希值为0.指向同一个对象的句柄的哈希值是一样的.


  句柄可以隐士的转换为布尔类型,你可以用下面的方法来测试是否节点/属性是空的:
  if (node) { ... } or if (!node) { ... } else { ... }.

  你也可以显式的检查xml_node/xml_attribute句柄是否为null:
  bool xml_attribute::empty() const;
  bool xml_node::empty() const;


  没有文档树,则节点和属性不能单独存在,在创建他们之前,你不能添加他们到某个文档.
  一旦节点/属性对象被销毁之后,指向他们的句柄将变得无效.这意味这entire tree的析构函数将使用所以的节点/属性句柄变得无限,它同样意味这销毁子树(通过调用xml_node::remove_chilid)或
  移除属性,使用对应的句柄无效.没有办法检查句柄的有效性,你需要通过额外机制来确认.

  Nodes and attributes do not exist without a document tree, so you can't create them without adding them to some document.
  Once underlying node/attribute objects are destroyed, the handles to those objects become invalid. While this means that destruction of the entire tree invalidates
  all node/attribute handles, it also means that destroying a subtree (by calling xml_node::remove_child) or removing an attribute invalidates the corresponding handles.
  There is no way to check handle validity; you have to ensure correctness through external mechanisms.

 


  Unicdoe接口:

   你可以使用UTF-8(also called char)接口,或UTF-16/32(also called wchar_t)接口之一使用库.
   这个选择可以通过PUGIXML_WCHAR_MODE定义,你可以在pugiconfig.hpp或预处理选项来设置.如果这个宏被定义,将使用wchar_t接口,否则(默认)使用char接口.
   库将使用wchar_t类型的大小来确定宽字符编码是否为UTF-16或UTF-32.

   注意:如果wchar_t的大小为2,pugixml使用UTF-16编码来代替UCS-2, which means that some characters are represented as two code points.

   树的所有函数处理字符串可以是c风格null结尾的字符串或使用STL的string,例如:
  const char* xml_node::name() const;
  bool xml_node::set_name(const char* value);

  and like this in wchar_t mode:

  const wchar_t* xml_node::name() const;
  bool xml_node::set_name(const wchar_t* value);


  There is a special type, pugi::char_t, that is defined as the character type and depends on the library configuration; it will be also used in the documentation hereafter.
  There is also a type pugi::string_t, which is defined as the STL string of the character type; it corresponds to std::string in char mode and to std::wstring in wchar_t mode.

  除了这些接口之外,内部的实现是,将XML数据存储为pugi:char_t
  In addition to the interface, the internal implementation changes to store XML data as pugi::char_t; this means that these two modes have different memory usage characteristics.
  The conversion to pugi::char_t upon document loading and from pugi::char_t upon document saving happen automatically, which also carries minor performance penalty.
 
  最后建议使用合适的字符模式,对于ASCII方式的XML数据可以使用UTF-8,否则可以使用wchar_t模式更合适.


  下面帮助函数有助于你在UTF-8和wchar_t编码之间进行转换.
  std::string as_utf8(const wchar_t* str);
  std::wstring as_wide(const char* str);

  这个两个函数接收一个null结尾的字符串作为参数,然后返回转换后的字符串为STL的字符串.
  as_utf8将指向UTF-16/32转换为UTF-8,as_wide将指向UTF-8到UTF-16/32的转换.
  在上面的转换中,无效的UTF序列将被无情的丢弃.str参数必须是一个有效的字符串,否则返回结果未知.这里还有两个重载:

  std::string as_utf8(const std::wstring& str);
  std::wstring as_wide(const std::string& str);

 


  线程安全保证:

  pugi库保证几乎所有的pugi函数在线程中有如下保证:

  1.可以安全的在多线程中调用free(非成员)函数.
  2.可以同时以只读的方式访问同一个树(所有const成员函数都不会修改树).
  3.可以同时的进行读/写访问,如果此时对树只有一个读或或一个写访问.


  同时修改和遍历树需要同步,例如,通过读写锁.修改是指修改文档结构和修改单独节点/属性的数据,例如改变names/values.

  set_memory_function是个例外,它修改全局变量,因此不是线程安全的. 该函数可以定制内存分配和释放指定代理.


  异常保证

  除XPath之外,pugixml它自身没有抛出任何异常.另外, 多数的pugixml函数也具有不抛异常的保证.

   Also functions that call user-defined callbacks (i.e. xml_node::traverse or xml_node::find_node) do not provide  any exception guarantees beyond the ones provided by the callback.

  If exception handling is not disabled with PUGIXML_NO_EXCEPTIONS define, XPath functions may throw xpath_exception on parsing errors; also, XPath functions may throw std::bad_alloc
  in low memory conditions. Still, XPath functions provide strong exception guarantee.


  使用默认构造函数来构造文档不会进行任何内存分配,文档节点在内存存储xml_document对象.
  当从文件或缓冲区加载文档时候,除非是使用原地加载函数(see Loading document from memory),否则将产生字符流的一个副本,所有节点和属性的name/value都从此缓冲区分配.这个缓冲区
  是分配的一个大的内存块,当文档销毁时候将释放该缓冲区.在分配该缓冲区之前,可能还需要分配一个缓冲区来做字符串编码转换缓冲区.

  所有额外的内存,例如文档结构(节点/属性对象)的内存都是分配在页(该库作者自己写的页池分配内存池)上,这些页大小为32kb,然后的一些小对象都在这页里分配.这写页只有在该页分配
  的所有对象都销毁后才能被释放,因而,销毁一个对象也不意味这随后创建的对象将重用同一个内存地址.
  This means that it is possible to devise a usage scheme which will lead to higher memory usage than expected; one example is adding a lot of nodes,
  and them removing all even numbered ones; not a single page is reclaimed in the process. However this is an example specifically crafted to produce unsatisfying behavior;
  in all practical usage scenarios the memory consumption is less than that of a general-purpose allocator because allocation meta-data is very small in size

 

  2.加载

  pugixml提供几个函数用于从不同地方(文件,c++ io流, 内存缓冲区)加载xml数据.这些函数使用超快非验证的解析器,这个解析器不是完全遵照W3C标准-它可以
  加载任何有效的XML文档,但没有执行某些well-formedness检查.虽然已经很努力来排除无效的XML文档,但为了性能的原因有些验证是没有完全执行的.
  一些XML转换(例如: EOL处理或属性值标准化)将导致解析速度,因此这些验证将没有处理.对于绝大多数XML文档的不同解析选项之间不存在性能上的差异.
  解析选项可以控制是否某些XML节点可以被继续,see parsing options for more information.

  在解析之前XML数据总是转换为内部字符格式(see uinicode接口).pugixml支持当前流行的Unicode编码(UTF-8, UTF-16(大小端), UTF-32(大小端),UCS-2也自然支持,因为
  它是UTF-16的子集.)并自动处理所有编码转换.加载函数通过检查XML数据的头部分自动进行编码检测,当然你也可以显式的指定了编码.多数情况下你不需要自己
  指定文档编码.编码转换在编码一节有详细描述.

  从文件加载文档

  多数的XML数据源可能要算是文件了;pugixml提供专门的函数从文件加载XML文档,如下:

  xml_parse_result xml_document::load_file(const char* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
  xml_parse_result xml_document::load_file(const wchar_t* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);

  这些函数接收文件路径作为第一个参数及两个可选参数,一个用于指定解析选项,另一个指定输入数据的编码类型.路径与目标操作系统格式相关,可以指定
  为绝对路径或相对路径.

  load_file首先销毁先前的文档树然后尝试从指定文件中加载新的文档树.操作的返回类型为xml_parse_result对象;这个对象包含操作状态和相关的信息(例如,如果
  解析失败,这里可以检查在文件中最后成功解析的位置).


  //一个例子
  pugi::xml_document doc;
  pugi::xml_parse_result result = doc.load_file("tree.xml");
  std::cout << "Load result: " << result.description() << ", mesh name: " << doc.child("mesh").attribute("name").value() << std::endl;

  从内存加载

  有些时候XML数据需要从其他源加载,例如HTTP URL;也许你希望通过非标准函数来提供XML数据,例如你虚拟文件系统或从gzip压缩的文件加载XML.这些方案都需要
  从内存中加载文档.首先你需要为XML数据提供一个连续的内存块;然后你可以调用缓冲区加载函数.如果需要,这些函数将处理编码转换,然后解析数据为XML文档树.
  这些缓冲区加载函数,都有不同的行为,因此有不同的性能和内存使用开销.
 
  xml_parse_result xml_document::load_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
  xml_parse_result xml_document::load_buffer_inplace(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
  xml_parse_result xml_document::load_buffer_inplace_own(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);

  这些函数接收缓冲区指针,该指针指向的内存块为XML数据,size用于指定缓冲区大小.同样,还有两个可选参数,用于指定解析选项和编码.缓冲区不必须要是zero-terminated.


  load_buffer函数从缓冲区加载,并且不改变的缓冲区,因此在解析之前需要创建另一个缓冲区,并把XML数据拷贝该缓冲区.执行拷贝操作可能导致性能损失.
  因此提供了load_buffer_inplace和load_buffer_inplace_own函数,它们将文档数据存储到该缓冲区,在处理时候修改它.为保持文档一直有效,你应该确

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值