clq

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

http://www.cnblogs.com/flying_bat/archive/2007/11/10/955327.html

http://tinyxpath.sourceforge.net/

--------------------------------------------------

TinyXML中文文档

译注:本文是TinyXML 2.5.2版本Document的中文文档,经原作者Lee Thomason同意由hansen翻译,如有误译或者错漏,欢迎指正。
版权:版权归原作者所有,翻译文档版权归本人hansen所有,转载请注明出处。
原文:http://www.grinninglizard.com/tinyxmldocs/index.html

 

 

TinyXml 文档

2.5.2

TinyXML

TinyXML是一个简单小巧,可以很容易集成到其它程序中的C++ XML解析器。

它能做些什么

简单地说,TinyXML解析一个XML文档并由此生成一个可读可修改可保存的文档对象模型(DOM)。

XML的意思是“可扩展标记语言“(eXtensible Markup Language)。它允许你创建你自己的文档标记。在为浏览器标记文档方面HTML做得很好,然而XML允许你定义任何文档标记,比如可以为一个组织者 应用程序定义一个描述“to do”列表的文档。 XML拥有一个结构化并且方便的格式,所有为存储应用程序数据而创建的随机文件格式都可以用XML代替,而这一切只需要一个解析器。

最全面正确的说明可以在http://www.w3.org/TR/2004/REC-xml-20040204/找到,但坦白地说,它很晦涩难懂。事实上我喜欢http://skew.org/xml/tutorial上关于XML的介绍。

有不同的方法可以访问和与XML数据进行交互。TinyXML使用文档对象模型(DOM),这意味着XML数据被解析成一个可被浏览和操作的C++ 对象,然后它可以被写到磁盘或者另一个输出流中。你也可以把C++对象构造成一个XML文档然后把它写到磁盘或者另一个输出流中。

TinyXML被设计得容易快速上手。它只有两个头文件和四个cpp文件。只需要把它们简单地加到你的项目中就行了。有一个例子文件——xmltest.cpp来引导你该怎么做。

TinyXML以Zlib许可来发布,所以你可以在开源或者商业软件中使用它。许可证更具体的描述在每个源代码文件的顶部可以找到。

TinyXML在保证正确和恰当的XML输出的基础上尝试成为一个灵活的解析器。TinyXML可以在任何合理的C++适用系统上编译。它不依赖于 异常或者运行时类型信息,有没有STL支持都可以编译。TinyXML完全支持UTF-8编码和前64k个字符实体(<i>译注:如果你不明 白这句译文,可能你需要了解一下Unicode编码</i>)。

它无法做些什么

TinyXML不解析不使用DTDs(文档类型定义)或者XSLs(可扩展样式表语言)。有其它解析器(到www.sourceforge.org 搜索一下XML)具有更加全面的特性,但它们也就更大,需要花更长的时间来建立你的项目,有更陡的学习曲线,而且经常有一个更严格的许可协议。如果你是用 于浏览器或者有更复杂的XML需要,那么TinyXML不适合你。

下面的DTD语法在TinyXML里是不做解析的:

 

<!DOCTYPE Archiv [
<!ELEMENT Comment (#PCDATA)>
]>

因为TinyXML把它看成是一个带着非法嵌入!ELEMENT结点的!DOCTYPE结点。或许这在将来会得到支持。

指南

有耐性些,这是一份能很好地指导你怎么开始的指南,它(非常短小精悍)值得你花时间完整地读上一遍。

代码状况

TinyXML是成熟且经过测试的代码,非常健壮。如果你发现了漏洞,请提交漏洞报告到sourcefore网站上 (www.sourceforge.net/projects/tinyxml)。 我们会尽快修正。

有些地方可以让你得到提高,如果你对TinyXML的工作感兴趣的话可以上sourceforge查找一下。

相关项目

你也许会觉得TinyXML很有用!(简介由项目提供)

特性

使用STL

TinyXML可以被编译成使用或不使用STL。如果使用STL,TinyXML会使用std::string类,而且完全支持 std::istream,std::ostream,operator<<和operator>>。许多API方法都有 ‘const char*’和’const std::string&’两个版本。

如果被编译成不使用STL,则任何STL都不会被包含。所有string类都由TinyXML它自己实现。所有API方法都只提供’const char*’传入参数。

使用运行时定义:

TIXML_USE_STL

来编译成不同的版本。这可以作为参数传给编译器或者在“tinyxml.h”文件的第一行进行设置。

注意:如果在Linux上编译测试代码,设置环境变量TINYXML_USE_STL=YES/NO可以控制STL的编译。而在Windows上, 项目文件提供了STL和非STL两种目标文件。在你的项目中,在tinyxml.h的第一行添加"#define TIXML_USE_STL"应该是最简单的。

UTF-8

TinyXML支持UTF-8,所以可以处理任何语言的XML文件,而且TinyXML也支持“legacy模式”——一种在支持UTF-8之前使用的编码方式,可能最好的解释是“扩展的ascii”。

正常情况下,TinyXML会检测出正确的编码并使用它,然而,通过设置头文件中的TIXML_DEFAULT_ENCODING值,TinyXML可以被强制成总是使用某一种编码。

除非以下情况发生,否则TinyXML会默认使用Legacy模式:

  1. 如果文件或者数据流以非标准但普遍的"UTF-8引导字节" (0xef 0xbb 0xbf)开始,TinyXML会以UTF-8的方式来读取它。
  2. 如果包含有encoding="UTF-8"的声明被读取,那么TinyXML会以UTF-8的方式来读取它。
  3. 如果读取到没有指定编码方式的声明,那么TinyXML会以UTF-8的方式来读取它。
  4. 如果包含有encoding=“其它编码”的声明被读取,那么TinyXML会以Legacy模式来读取它。在Legacy模式下,TinyXML会像以前那样工作,虽然已经不是很清楚这种模式是如何工作的了,但旧的内容还得保持能够运行。
  5. 除了上面提到的情况,TinyXML会默认运行在Legacy模式下。

如果编码设置错误或者检测到错误会发生什么事呢?TinyXML会尝试跳过这些看似不正确的编码,你可能会得到一些奇怪的结果或者乱码,你可以强制TinyXML使用正确的编码模式。

通过使用LoadFile( TIXML_ENCODING_LEGACY )或者LoadFile( filename, TIXML_ENCODING_LEGACY ), 你可以强制TinyXML使用Legacy模式。你也可以通过设置TIXML_DEFAULT_ENCODING = TIXML_ENCODING_LEGACY来强制一直使用Legacy模式。同样的,你也可以通过相同的方法来强制设置成 TIXML_ENCODING_UTF8。

对于使用英文XML的英语用户来说,UTF-8跟low-ASCII是一样的。你不需要知道UTF-8或者一点也不需要修改你的代码。你可以把UTF-8当作是ASCII的超集。

UTF-8并不是一种双字节格式,但它是一种标准的Unicode编码!TinyXML当前不使用或者直接支持wchar,TCHAR,或者微软的 _UNICODE。"Unicode"这个术语被普遍地认为指的是UTF-16(一种unicode的宽字节编码)是不适当的,这是混淆的来源。

对于“high-ascii”语言来说——几乎所有非英语语言,只要XML被编码成UTF-8, TinyXML就能够处理。说起来可能有点微妙,比较旧的程序和操作系统趋向于使用“默认”或者“传统”的编码方式。许多应用程序(和几乎所有现在的应用 程序)都能够输出UTF-8,但是那些比较旧或者难处理的(或者干脆不能使用的)系统还是只能以默认编码来输出文本。

比如说,日本的系统传统上使用SHIFT-JIS编码,这种情况下TinyXML就无法读取了。但是一个好的文本编辑器可以导入SHIFT-JIS的文本然后保存成UTF-8编码格式的。

Skew.org link上关于转换编码的话题做得很好。

测试文件“utf8test.xml”包含了英文、西班牙文、俄文和简体中文(希望它们都能够被正确地转化)。“utf8test.gif”文件是 从IE上截取的XML文件快照。请注意如果你的系统上没有正确的字体(简体中文或者俄文),那么即使你正确地解析了也看不到与GIF文件上一样的输出。同 时要注意在一个西方编码的控制台上(至少我的Windows机器是这样),Print()或者printf()也无法正确地显示这个文件,这不关 TinyXML的事——这只是操作系统的问题。TinyXML没有丢掉或者损坏数据,只是控制台无法显示UTF-8而已。

实体

TinyXML认得预定义的特殊“字符实体”,即:

 

&amp; &
&lt; <
&gt; >
&quot; "
&apos; ‘

这些在XML文档读取时都会被辨认出来,并会被转化成等价的UTF-8字符。比如下面的XML文本:

 

Far &amp; Away

从TiXmlText 对象查询出来时会变成"Far & Away"这样的值,而写回XML流/文件时会以“&amp;”的方式写回。老版本的TinyXML“保留”了字符实体,而在新版本中它们会被转化成字符串。

另外,所有字符都可以用它的Unicode编码数字来指定, " "和" "都表示不可分的空格字符。

打印

TinyXML有几种不同的方式来打印输出,当然它们各有各的优缺点。

    • Print( FILE* ):输出到一个标准C流中,包括所有的C文件和标准输出。
      • "相当漂亮的打印", 但你没法控制打印选项。
      • 输出数据直接写到FILE对象中,所以TinyXML代码没有内存负担。
      • 被Print()和SaveFile()调用。

 

  • operator<<:输出到一个c++流中。
    • 与C++ iostreams集成在一起。
    • 在"network printing"模式下输出没有换行符,这对于网络传输和C++对象之间的XML交换有好处,但人很难阅读。
  • TiXmlPrinter:输出到一个std::string或者内存缓冲区中。
    • API还不是很简练。
    • 将来会增加打印选项。
    • 在将来的版本中可能有些细微的变化,因为它会被改进和扩展。

设置了TIXML_USE_STL,TinyXML就能支持C++流(operator <<,>>)和C(FILE*)流。但它们之间有些差异你需要知道:

C风格输出:

  • 基于FILE*
  • 用Print()和SaveFile()方法

生成具有很多空格的格式化过的输出,这是为了尽可能让人看得明白。它们非常快,而且能够容忍XML文档中的格式错误。例如一个XML文档包含两个根元素和两个声明仍然能被打印出来。

C风格输入:

  • 基于FILE*
  • 用Parse()和LoadFile()方法

速度快,容错性好。当你不需要C++流时就可以使用它。

C++风格输出:

  • 基于std::ostream
  • operator<<

生成压缩过的输出,目的是为了便于网络传输而不是为了可读性。它可能有些慢(可能不会),这主要跟你系统上ostream类的实现有关。无法容忍格式错误的XML:此文档只能包含一个根元素。另外根级别的元素无法以流形式输出。

C++风格输入:

  • 基于std::istream
  • operator>>

从流中读取XML使其可用于网络传输。通过些小技巧,它知道当XML文档读取完毕时,流后面的就一定是其它数据了。TinyXML总假定当它读取到 根结点后XML数据就结束了。换句话说,那些具有不止一个根元素的文档是无法被正确读取的。另外还要注意由于STL的实现和TinyXML的限 制,operator>>会比Parse慢一些。

空格

对是保留还是压缩空格这一问题人们还没达成共识。举个例子,假设‘_’代表一个空格,对于"Hello____world",HTML和某些XML 解析器会解释成"Hello_world",它们压缩掉了一些空格。而有些XML解析器却不会这样,它们会保留空格,于是就是 “Hello____world”(记住_表示一个空格)。其它的还建议__Hello___world__应该变成Hello___world 。

这是一个解决得不能让我满意的问题。TinyXML一开始就两种方式都支持。调用TiXmlBase::SetCondenseWhiteSpace( bool )来设置你想要的结果,默认是压缩掉多余的空格。

如果想要改变默认行为,你应该在解析任何XML数据之前调用TiXmlBase::SetCondenseWhiteSpace( bool ) ,而且我不建议设置之后再去改动它。

句柄

想要健壮地读取一个XML文档,检查方法调用后的返回值是否为null是很重要的。一种安全的检错实现可能会产生像这样的代码:

 

TiXmlElement* root = document.FirstChildElement( "Document" );
if ( root )
{
    TiXmlElement* element = root->FirstChildElement( "Element" );
    if ( element )
    {
        TiXmlElement* child = element->FirstChildElement( "Child" );
        if ( child )
        {
            TiXmlElement* child2 = child->NextSiblingElement( "Child" );
            if ( child2 )
            {
                // Finally do something useful.

用句柄的话就不会这么冗长了,使用TiXmlHandle类,前面的代码就会变成这样:

 

TiXmlHandle docHandle( &document );
TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).ToElement();
if ( child2 )
{
    // do something useful

这处理起来容易多了。 查阅TiXmlHandle可以得到更多的信息。

行列追踪

对于某些应用程序来说,能够追踪节点和属性在它们源文件中的原始位置是很重要的。另外,知道解析错误在源文件中的发生位置可以节省大量时间。

TinyXML能够追踪所有结点和属性在文本文件中的行列原始位置。TiXmlBase::Row() 和 TiXmlBase::Column() 方法返回结点在源文件中的原始位置。正确的制表符号可以经由TiXmlDocument::SetTabSize() 来配置。

使用与安装

编译与运行xmltest:

提供了一个Linux Makefile和一个Windows Visual C++ .dsw 文件。只需要简单地编译和运行,它就会在你的磁盘上生成demotest.xml文件并在屏幕上输出。它还尝试用不同的方法遍历DOM并打印出结点数。

那个Linux makefile很通用,可以运行在很多系统上——它目前已经在mingw和MacOSX上测试过。你不需要运行 ‘make depend’,因为那些依赖关系已经硬编码在文件里了。

用于VC6的Windows项目文件

  • tinyxml: tinyxml 库,非STL
  • tinyxmlSTL: tinyxml 库,STL
  • tinyXmlTest: 用于测试的应用程序,非STL
  • tinyXmlTestSTL: 用于测试的应用程序,STL

Makefile

在makefile的顶部你可以设置:

PROFILE,DEBUG,和TINYXML_USE_STL。makefile里有具体描述。

在tinyxml目录输入“make clean”然后“make”,就可以生成可执行的“xmltest”文件。

在某一应用程序中使用:

把tinyxml.cpp,tinyxml.h, tinyxmlerror.cpp, tinyxmlparser.cpp, tinystr.cpp, 和 tinystr.h 添加到你的项目和makefile中。就这么简单,它可以在任何合理的C++适用系统上编译。不需要为TinyXML打开异常或者运行时类型信息支持。

TinyXML怎么工作

举个例子可能是最好的办法,理解一下:

 

<?xml version="1.0" standalone=no>
<!– Our to do list data –>
<ToDo>
<Item priority="1"> Go to the <bold>Toy store!</bold></Item>
<Item priority="2"> Do bills</Item>
</ToDo>

它称不上是一个To Do列表,但它已经足够了。像下面这样读取并解析这个文件(叫“demo.xml”)你就能创建一个文档:

TiXmlDocument doc( "demo.xml" );
doc.LoadFile();

现在它准备好了,让我们看看其中的某些行和它们怎么与DOM联系起来。

 

<?xml version="1.0" standalone=no>

第一行是一个声明,它会转化成TiXmlDeclaration 类,同时也是文档结点的第一个子结点。

这是TinyXML唯一能够解析的指令/特殊标签。一般来说指令标签会保存在TiXmlUnknown 以保证在它保存回磁盘时不会丢失这些命令。

 

<!– Our to do list data –>

这是一个注释,会成为一个TiXmlComment对象。

 

<ToDo>

"ToDo"标签定义了一个TiXmlElement 对象。它没有任何属性,但包含另外的两个元素。

 

<Item priority="1">

生成另一个TiXmlElement对象,它是“ToDo”元素的子结点。此元素有一个名为“priority”和值为“1”的属性。

 

Go to the

TiXmlText ,这是一个叶子结点,它不能再包含其它结点,是"Item" TiXmlElement的子结点。

 

<bold>

另一个TiXmlElement, 这也是“Item”元素的子结点。

等等

最后,看看整个对象树:

 

TiXmlDocument "demo.xml"
TiXmlDeclaration "version=’1.0′" "standalone=no"
TiXmlComment " Our to do list data"
TiXmlElement "ToDo"
TiXmlElement "Item" Attribtutes: priority = 1
TiXmlText "Go to the "
TiXmlElement "bold"
TiXmlText "Toy store!"
TiXmlElement "Item" Attributes: priority=2
TiXmlText "Do bills"

 

文档

本文档由Doxygen使用‘dox’配置文件生成。

许可证

TinyXML基于zlib许可证来发布:

本软件按“现状”提供(即现在你看到的样子),不做任何明确或隐晦的保证。由使用此软件所引起的任何损失都决不可能由作者承担。

只要遵循下面的限制,就允许任何人把这软件用于任何目的,包括商业软件,也允许修改它并自由地重新发布:

1. 决不能虚报软件的来源;你决不能声称是你是软件的第一作者。如果你在某个产品中使用了这个软件,那么在产品文档中加入一个致谢辞我们会很感激,但这并非必要。

2. 修改了源版本就应该清楚地标记出来,决不能虚报说这是原始软件。

3. 本通告不能从源发布版本中移除或做修改。

参考书目

万维网联盟是定制XML的权威标准机构,它的网页上有大量的信息。

权威指南:http://www.w3.org/TR/2004/REC-xml-20040204/

我还要推荐由OReilly出版由Robert Eckstein撰写的"XML Pocket Reference"……这本书囊括了入门所需要的一切。

捐助者,联系人,还有简史

非常感谢给我们建议,漏洞报告,意见和鼓励的所有人。它们很有用,并且使得这个项目变得有趣。特别感谢那些捐助者,是他们让这个网站页面生机勃勃。

有很多人发来漏洞报告和意见,与其在这里一一列出来不如我们试着把它们写到“changes.txt”文件中加以赞扬。

TinyXML的原作者是Lee Thomason(文档中还经常出现“我”这个词) 。在Yves Berquin,Andrew Ellerton,和tinyXml社区的帮助下,Lee查阅修改和发布新版本。

我们会很感激你的建议,还有我们想知道你是否在使用TinyXML。希望你喜欢它并觉得它很有用。请邮寄问题,评论,漏洞报告给我们,或者你也可登录网站与我们取得联系:

www.sourceforge.net/projects/tinyxml

Lee Thomason, Yves Berquin, Andrew Ellerton

--------------------------------------------------

http://blog.csdn.net/hoyt00/article/details/6769883

使用TinyXml库值得注意的几个地方

分类: C++ 659人阅读 评论(9) 收藏 举报

      这两天仔细看了下TinyXml的源代码, 完美地搞清楚了一些网友和我自己的很多疑问. 鉴于TinyXml的实用性, 而且现在不少人在使用, 就决定在此做点有意义的事情 ---- 列出使用TinyXml库值得注意的几个地方.

     关于TinyXml库的介绍网上有很多资料, 大家可以试着搜下, 这里我就不多说了, TinyXml很小巧, 但它提供了非常丰富的接口, 特别适用于存取程序的数据, 如果你使用它, 相信你会感觉到它的灵活的大笑.

     TinyXml下载地址: http://download.csdn.net/detail/hoyt00/3904805

                                http://sourceforge.net/projects/tinyxml/

 

     以下几点值得注意哦:

 

1.new出指针而不见delete.

     如 果你看了TinyXml的文档或者一些网友写的例子程序, 你会发现, 其中new出了很多对象而不见一个delete, 这个问题我当时也感觉特别难以接受, 网上也有各种各样的说法, 有说"nwe出这么多, 内存不泄漏才怪", 有说"TinyXml的指针有自销毁功能"等等, 这些说法都是错误的, 只要你正确的使用TinyXml, 无论你new多少(当然在内存的有效使用范围之内)都不会有内存泄漏, 而且TinyXml的指针根本没有自销毁功能, 可以想像一下, 在C++中一个没有经过任何类的包装的指向堆内存的指针变量怎么可能在它自身的生命结束时管得了它所指向的对象? 那它到底是怎么回事?
     我 们都知道(这里我假设你已经看过TinyXml文档), TinyXml是利用DOM(文档对象模型)来呈现XML文档的, 在它眼中XML文档就是一棵树, TiXmlDocument对象就是这棵树的根结点, 在一个完整的文档中, 除了它, 其余结点必须都是它的后代, 所以TinyXml用了一个很巧妙的方法来析构每一个结点所对应的对象 ---- 每个结点的析构任务都委托给了它的父亲, 这样只要保证父亲被正确析构, 或者调用了父亲的Clear函数, 它的所有后代都会被正确的析构, 所以对整个文档来说只要TiXmlDocument对象被正确析构, 那就万无一失了, 这棵树会先从叶子销毁, 一直到树根. 像这种数据结构, 为了保证树的完整性而使用堆内存, 和由它自己管理内存也是理所当然的, 由此便可得到以下几个结论:
     (1)TiXmlDocument对象最好在栈上创建, 如果在堆上创建了, 那你必须得自己销毁它, 千万不要像别的对象一样new出了就不管了.
     (2)除了TiXmlDocument对象, 树中的别的结点对象, 必须是堆上创建的, 千万不要把栈上对象的地址链接(LinkEndChild)到树中, 因为栈上对象是不能用delete销毁的, 当然TinyXml也有对栈上对象插入的方法, 以下会说到.
     (3) 除了文档结点, new出的所有结点对象必须被链接到一个父亲结点上, 才可以不用管对象的delete, 而且必须不能管, 不然有可能整棵树会被破坏而得不到正确的遍历和析构, 如果new出的对象从来没链接到某棵树上, 而且将来也不打算链接, 无论有没有别的结点链接到它身上, 你都必须手动delete它.
     (4)不要尝试链接已经被别的结点链接过的指针, 很显然, 这会造成无法估量的麻烦, 不用多说, 大家都懂的.

 

2.Insert vs. Link.

     当 然不是所有人都喜欢new, 可能他们更喜欢像std::vector那样插入元素的副本而不是交给它一个指针, TinyXml也提供了同样的方式插入结点 ---- Insert函数(InsertEndChild, InsertBeforeChild, InsertAfterChild), 因为这些函数插入的是结点的副本(包括所有子结点) ,所以可以传给它栈上或堆上的对象, 但是要注意, 如果传入的是堆上对象, 那还必须手动delete它, 而不像LinkEndChild那样链接了就不能管了, 因为树析构的时候析构的是它的副本, 而不是它. 我更倾向于使用Link + 堆上对象, 而不Insert + 栈上对象, 因为这样可以省去创建副本的运行成本, 除非要插入到具体的位置, 而不是End, 当然也可以修改源代码使Link函数也有这种功能.

 

3.元素的属性不是普通结点.

     与 文本对象(TiXmlText)不同, 属性对象(TiXmlAttribute)在文档树中不是以普通结点形式存在的, 也不从TiXmlNode类派生, 而是被元素结点的数据成员 ---- 一个TiXmlAttributeSet对象所管理, 这个TiXmlAttributeSet对象持有一个环状的TiXmlAttribute对象链表(具体可以参看源代码), 所以TiXmlElement对象通过调用Child系列函数是无法得到属性的, 相反这件事是通过Attribute系列函数完成的. 访问器(从TiXmlVisitor派生的用户实现的类对象, 一种附加的回调机制, 具体参看源代码)没有针对TiXmlAttribute对象的接口, 而必须在对TiXmlElement对象的实现中处理属性, 因此TiXmlAttribute类也没有Accept虚函数.

 

4.尽量不要在文档中使用中文.

     TinyXml只认识UTF-8和ISO 8859-1编码, 而不知GB2312为何物, 但事实上你以GB2312在文档中写入中文, 之后可以正确读取, 而且文档在记事本中打开也能显示正确的中文, 其实这是种巧合, 并不是TinyXml支持GB2312了.
     这 个问题需要解释下, 我已经仔细分析过了, TinyXml的函数有char*类型参数, 而没有wchar_t*类型参数, 所以直接在程序中向文档写入中文必然是GB2312方式(这里是以VC编译器为例的), 这时char*只是指向一块内存块, 跟void*一样, 这块内存只有用GB2312才能正确解释为中文, 因为TinyXml是被设计跨平台的, 所以不要指望它会调用WideCharToMultiByte和MultiByteToWideChar来帮你做转换, 而以GB2312写入的中文在读取时这些中文字符的码值是不变的, 也就是你在准备写入文档时是码值是多少, 读取到程序里的值就是多少, 而把这个值当作GB2312编码时就是原来写文档时的中文字符了, 当把写入的文档在记事本打开时, 由于没有utf-8标记字节0xEF 0xBB 0xBF(TinyXml默认不写入这三个字节, 稍后再说怎么让它写入), 所以记事本把xml文件当作GB2312编码打开, 就阴差阳错地把原本错误的字节以正确的中文字符显示了, 但终究这篇xml文档是utf-8编码的, 那些中文字符本应显示为乱码的, 就这样错误的写入, 错误的读取, 记事本错误的判断, 都加到一起就离奇地没有错误了.
     但错误还是错误, 有一个办法, 就是在文档头部加上utf-8标记字节0xEF 0xBB 0xBF, 这样记事本就能正确判断文档编码, 正确地以utf-8打开, 正确地把中文显示为乱码.
     基于这几种错误叠加的现象, 如果你生成的xml文档只用在你自己特定的程序中 而不用在其它软件(比如有时要用别的文本处理软件打开查看内容), 那么在文档中存取中文是完全没有问题的, 但在别的地方以utf-8打开时中文就是乱码.
     保证所有地方都正确的方法是在写入时和读取时用WideCharToMultiByte和MultiByteToWideChar把GB2312编码的中文字串转换为UTF-8编码的中文字串, 如此, 所有软件都能正确的读取UTF-8编码的中文字符(为了让记事本正确的判断为UTF-8, 可以加上utf-8标记字节, 虽然它不是标准, 但普遍使用).当然还是那句话 ---- 尽量不使用中文和其它非英文字符, 除非迫不得已.

 

 

另外我在看源代码时, 有几个地方也发表下自己的想法:

 1.关于explicit关键字

     tinystr.h第85行  TIXML_EXPLICIT TiXmlString ( const char * str, size_type len) : rep_(0)

     (TIXML_EXPLICIT宏的内容是explicit) explicit关键字的作用是使单参数的构造函数不用作隐式转换, 但这个构造函数有两个参数, 很显然, 它根本就没有发生隐式转换的可能, 也完全没有必要使用explicit关键字.


2.关于成员函数const和非const

     TinyXml的类库中有许多类的函数有const和非const版本, 为什么非const版本的函数不把函数体重新写一遍而是用const_cast把const函数的const性质转换掉再调用它呢? 其实函数体也就返回数据成员的那一两句而已.


3.关于默认实参

     有些类的重载成员函数们, 一个函数的参数表和另一个函数的参数表的前面部分完全一致, 为什么不使用默认实参而是参数较少的函数版本调用参数较多的函数版本呢?

 

4.关于Test项目中的乱码

     这也是很多人都疑惑的一个问题 ---- TinyXml库自带Test项目竟然编译不过, 这个问题的原因很简单, 打开xmltest.cpp文件, 定位到第1141行, 你会发现, 该行和下面几行有乱码, 这又是怎么回事?

     其实这几行是作者为测试ISO 8859-1编码(一种单字节编码, 编码范围使用了8位的所有取值, 也就是0到0xFF)文档而写, 意图把ISO 8859-1字符串写入文档对象, 再从文档对象读取, 只有用ISO 8859-1编码才能正确解释.

     但 是编译器以GB2312打开cpp文件时遇到这几个字符就发生了奇怪的事情 ---- 源码变乱码了. 本来乱码没什么的, 大不了我输出到xml文件也乱码就是了, 但问题是那几个字符不仅使它本身乱, 而且它后面的一个asc2字符也有50%的几率跟着遭殃, 因为我们都知道用GB2312解码字符串时如果第一个字节大于0x7F, 一般情况下会把它和接下来的一个字节当作一个整体字符, 当然这个字符可能显示, 也可能不显示, 无论怎样都不是正确的, 所以你会看到那些字串中有用于xml元素的"<"符号而没有">"符号, 有的字串甚至都没反引号, 这时不要简单的加上反引号, 虽然语法正确了, 能编译, 但xml语法还没纠正, 调试时会导致文档对象里的断言失败而退出. 另外我还查看了这几个乱码字符的二进制值:

第1141行 "<?"
二进制值 22 3C E4 3E 22 0A
GB2312 "<(\xE4\x3E)"
ISO 8859-1 "<ä>"

 

第1142行 "C鰊t鋘t咪鳇闹?
二进制值 22 43 F6 6E 74 E4 6E 74 DF E4 F6 FC C4 D6 DC 22 0A
GB2312 "C(\xF6\x6E)t(\xE4\x6E)t(\xDF\xE4)(\xF6\xFC)(\xC4\xD6)(\xDC\x22)
ISO 8859-1                                                "CöntäntßäöüÄÖÜ"

 

第1143行 "</?";
二进制值 22 3C 2F E4 3E 22 3B 0A
GB2312 "</(\xE4\x3E)";
ISO 8859-1       "</ä>";

其中二进制值省略了行首的空白字符, GB2312的括号中两个字节表示被当作的一个整体,ISO 8859-1表示原本用ISO 8859-1编码时显示的字串. 可以看出以ISO 8859-1编码的文本是完全符合C++语法和xml语法的, 大家在生成项目时将这块代码注释掉就能通过编译和调试了.

     还有刚才提到TinyXml保存文档时默认不写入utf-8标记字节BOM, 如果想让它保存时写入这三个字节, 可以修改TiXmlDocument类添加一个成员:

  1. public:  
  2.     void SetMicrosoftBOM(bool _useBOM) {useMicrosoftBOM = _useBOM;}  

当然也可以修改别的成员函数, 比如给SaveFile函数添加一个bool参数_useBOM等等, 看个人爱好了.


     关于TinyXml的使用就先写到这了, 如果有不对的地方, 希望大家指出, 如果有不明白的地方也可以联系我!

posted on 2012-01-06 09:29  clq  阅读(2708)  评论(1编辑  收藏  举报