tinyxml 代码解读:
tinyxml 是开源c++软件, 递归代码的特点: 易写易读难调.
甲:分析一下Doc.Print() 函数
Print()函数是一个重载函数,每个对象都有自己的Print() 函数.
doc print, 就是把每个结点都print 一遍。
void TiXmlDocument::Print( FILE# cfile, int depth ) const
{
for ( const TiXmlNode# node=FirstChild(); node; node=node->NextSibling() )
{
node->Print( cfile, depth );
fprintf( cfile, “\n” );
}
}
声明,注释,文本都是结点,这些结点打印很简单,打印出来就可以了。
1. 声明的打印: 是一个virtual 虚函数,打印出保留的version,encoding,alone就可以了。
2. 注释的打印: 是一个virtual 虚函数,打印出保留的text 就可以了, text 就保存在node 的 value 中
3. 文本的打印: 是一个virtual 虚函数,打印出保留的text 就可以了, text 就保存在node 的 value 中
4. 元素结点的打印
这是关键,因为它牵扯到递归。也牵扯属性, 代码写的看起来很简单,因为是递归,调试起来比较复杂.
分析:
4.1. 首先根据depth 深度打印空格
4.2. 然后打印元素开始<加value
4.3. 如果有属性,再打印属性
4.4. 如果没有child,则打印 /> 结束
4.5. 如果有child, 如果child结点不包含文本,则先打印一个回车,再打印child 结点,深度会加1(这里是递归)
4.6. 打印换行后再打印对齐空格,再打印尾巴</加value加>来结束
这里面对于只有一个child 的情况,有一个打印上的优化,少打一个回车使看起来更舒服.
5. 属性的打印.
属性不是一个node. 所以它不在上面doc.Print 的循环中,但它是元素的属性
属性是键值对,打印它的name,value即可,value需要用双引号括起来, 如果value中包含双引号,那就用单引号括起来。
乙:分析一下Doc.Parse() 函数
while ( p && #p )
{
TiXmlNode# node = Identify( p, encoding ); // 先判断出是哪种类型的结点. 都用其基类TiXmlNode来指向具体的类型
if ( node )
{//Parse 是基类TiXmlBase的虚函数(接口函数), TiXmlNode 并无Parse函数,但具体的Node类型实现了Pase函数
p = node->Parse( p, &data, encoding );
LinkEndChild( node ); //把分析到的node 链接起来
}
}
1. Identify() 是如何判定类型的.
首先必需以< 开头, 然后以 <?xml 开始,表示是一个声明 <!-- 开始,表示是一个注释 <! 开始,表示是一个Dtd
头 <![CDATA[" 开始, 表示是一个cdata头 后两种不太常见, 其它类型alpha字母及下划线开始就是XML元素,例如:
<Item 非此类型就是unkown类型的
2. 各种类型的分析
2.1 : 声明的分析. 它需要调用属性分析,分析出version,encoding,standalone的值
2.2 : 注释的分析. 它把
为止所有的东西都保存到value中
2.3 : 元素的分析.
2.3.1: 首先它要读取名字到value中
2.3.2: 如果后面跟的是/> ,则结束分析,返回
2.3.3: 如果后面跟的是> ,则要读取value 到 data,然后后面应该是endtag及>否则格式错误
2.3.4: 否则跟的是属性, 则读取属性,并把属性加到属性集中,再继续分析.
3. 属性的读取:
声明一个属性对象,然后进行分析.
读取名字ReadName(),然后碰到一个’=',然后读取文本ReadText(), 分别存储到name 和 value
4. value的读取 ReadValue()
4.1 如果不是<开始,则说明是一个文本,
TiXmlText# textNode = new TiXmlText( "" );
p = textNode->Parse( p, data, encoding );
LinkEndChild( textNode );
4.2 是< 开始, 递归调用. 得到一个结点表示,分析
TiXmlNode# node = Identify( p, encoding );
p = node->Parse( p, data, encoding );
LinkEndChild( node );
参考代码:
#include <iostream>
#include <sstream>
using namespace std;
#include "tinyxml.h"
int main()
{
const char* demoStart =
"<?xml version=\"1.0\" standalone='no' >\n"
"<!-- ToDo is the root element, it must be exist -->"
"<ToDo>\n"
"<!-- Do I need </Item to end element ? -->\n"
"<Item priority=\"2\" distance='middle' time='星期1'> 游香山 </Item>"
"<Item priority=\"1\" distance='short' time='星期2'> 逛故宫 </Item>"
"<Item priority=\"1\" distance='far' time='星期3'> 去 <bold>长城</bold> </Item>"
"</ToDo>";
//代码简单,跟踪调试才是硬道理!
TiXmlDocument doc( "demotest.xml" ); //创建一个文档,名称为demotest.xml
doc.Parse( demoStart ); //将字符串分析进该文档,成为一个个结点 (递归分析,比较麻烦!)
if ( doc.Error() )
{
printf( "Error in %s: %s\n", doc.Value(), doc.ErrorDesc() );
exit( 1 );
}
doc.Print(); //文档向屏幕输出, 递归打印!(比较麻烦)
// doc.SaveFile(); //保留文档到文件
return 0;
}