读书人

开源个小弟我自己写的简单xml解析器~

发布时间: 2012-12-27 10:17:10 作者: rapoo

开源个我自己写的简单xml解析器~~~~
已经自我放逐好几年了.打算去上班得了.在最后的自由日子里,做点有意义的事吧...

先来下载地址
http://www.kuaipan.cn/file/id_12470514853353274.htm

已经在很多正式,非正式的场合用过了.干脆开源得了.BSD授权.
代码比较久远,最后一次修改也在4~5年前了.写的比较BT,只有gcc/vc/icl能编译...
不过性能和易用性还是不错的.之前我测试过的,只有几个in place的xml解析器稍微比我的快一点点.
下面是示例代码:

// xml-test.cpp
#include <stdio.h>
#include "xml.h"

// 自定义FILE*输出策略
class cfile_writer {
FILE *_fp;
cfile_writer &operator = ( const cfile_writer& );
cfile_writer( const cfile_writer& );
public:
cfile_writer( FILE *fp ) : _fp(fp) {
fputs( "-----------------\n", _fp );
}
~cfile_writer() {
fputs( "\n", _fp );
}
// 策略不用预分配空间
// 如果为1, 在输出前会计算要占空间大小, 并调用resize接口进行预分配.
static const int need_pre_allocate = 0;
// 预分配接口
bool resize( size_t size ) const { return true; }
// 输出一个字符
void write( char value ) const {
fputc( value, _fp );
}
// 输出一个字符串, 长度由size指定
void write( const char *value, size_t size ) const {
fwrite( value, 1, size, _fp );
}
};

int main() {
using namespace cpp::utils;
const char xml_string[] = "<root attr=\"root attr\"><node prop=\"234\"/>text content<!-- comment --></root>";
xml x;

// 解析xml_string, 用不同的reader策略可以从不同的源中读数据
// 也可以自定义读策略, 以适应不同的需求
// 解析成功返回true.如果只有部分解析成功时虽然返回false,但已经解析成功的内容仍然可用
// 如果宏XML_WITH_PARSE_STATUS设置为1(默认为0).可以从x.info()中得到解析器停止的位置,方便调试.但会降低解析器性能.
x.parse( xml_reader( xml_string ) );

xml x2;
x2.push_back( xml::tag("root-x2") );// 直接向空xml对象中添加标签
x2("root-x2").push_back( xml::text("text value") );
x2.write( cfile_writer( stderr ) );

// 输出/root/node[prop]的值
// ()运算符为标签查找,返回指定名称的第一个标签.[]运算符为属性查找,返回指定名称的属性.
printf( "/root/node[prop] = [%s]\n", x("root")("node")["prop"].value().c_str() );
// 这里使用了null object模式,所以无需检查每一步的返回结果,不会因为访问非法节点而产生异常.简化使用
printf( "null object test:[%s]\n", x("roxxot")("noeede")["prop"].value().c_str() );

// 把root标签转成其迭代器(&运算符)
xml::tag_iterator root_tag = &x("root");

// 迭代所有子节点
for( xml::node_iterator node = x.root()->begin(); node != x.root()->end(); ++node ) {
// xml::node_iterator为通用节点迭代器, 可以指向任何类型的节点.
// 并可以转型成任意的其它迭代器. 但如果指向的节点类型和目标迭代器类型不符, 则会自动指向下一个合法的节点
// 比如: <abc/><!--comment-->
// 这里有两个节点,一个abc标签,一个注释.
// 如果有当前node迭代器指向abc标签.
//把node转成xml::tag_iterator类型时,则指向的节点不变.
//如果转成xml::comment_iterator时则会指向后面的注释.
//如果转成其它不存在类型的节点,则会指向容器的末尾.
printf( "node type: %d\t", node->type );
switch( node->type ) {
case xml::_TYPE_TAG:
printf( "tag name:%s\n", xml::tag_iterator(node)->name().c_str() );
break;
case xml::_TYPE_COMMENT:
printf( "comment:%s\n", xml::comment_iterator(node)->text().c_str() );
break;
case xml::_TYPE_TEXT:
printf( "text:%s\n", xml::text_iterator(node)->text().c_str() );


break;
case xml::_TYPE_ATTRIBUTE:
printf( "attribute:%s=%s\n", xml::attribute_iterator(node)->name().c_str(), xml::attribute_iterator(node)->value().c_str() );
break;
default:
printf( "unknown type\n" );
break;
}
};

// 迭代所有子标签
for( xml::tag_iterator tag = x.root()->begin(); tag != x.root()->end(); ++tag ) {
// 专用类型的迭代器只能遍历此类型的节点
printf( "tag:%s\n", tag->name().c_str() );
}

// 在/root/node下添加abc标签, 并保存指向标签的迭代器
xml::tag_iterator abc_tag = x("root")("node").push_back( xml::tag( "abc" ) );
// 用abc_tag迭代器向abc标签添加属性
abc_tag->push_back( xml::attribute( "tag-prop", "value abcdefg" ) );
// 在abc标签前插入注释
abc_tag->parent().insert( abc_tag, xml::comment( "tag-prop comment" ) );
// 把xml_string解析出来,并将结果放到abc_tag所指向的标签里
abc_tag->parse( xml_reader( xml_string ) );
// 深拷贝x2对象中的根节点到abc标签中,实现跨xml对象进行节点复制
abc_tag->push_front_copy( x2.root() );
// 输出abc_tag指向的标签, 第二个参数true表示只输出内容标签的内容,不包含标签本身及属性
abc_tag->write( cfile_writer( stdout ), true );
// 删除第一个子节点
abc_tag->erase( abc_tag->begin() );
abc_tag->write( cfile_writer( stdout ), true );
// 不能直接删除孙节点
x.erase( xml::tag_iterator(abc_tag->begin()) );// 转型成xml::tag_iterator是因为abc的第一个节点是属性.删除不直观.用标记会明显点
abc_tag->write( cfile_writer( stdout ) );// 没有删掉
// 递归删除可以成功
x.recursion_erase( xml::tag_iterator(abc_tag->begin()) );
abc_tag->write( cfile_writer( stdout ) );// 已经删除成功
return 0;
}

只支持基本语法,很多东西不支持.比如:CDATA不支持,自定义转意也不支持...
用来做配置文件还是不错.
[最优解释]
谢谢分享,楼主太好了
[其他解释]
貌似不错,感谢分享
[其他解释]
顶顶,感谢分享
[其他解释]
这种基础功能从来不自己写的人路过必须顶一下
[其他解释]
支持一下,多多分享。
[其他解释]
like it ,do it !
[其他解释]
谢谢分享!
[其他解释]
辛苦了
[其他解释]
不知道支持unicode不,如果只是支持ascii,那开源的已经很多了
[其他解释]
支持简体汉字、繁体汉字、韩文、……吗?(^_^)
[其他解释]
支持一个
[其他解释]
支持奉献精神!
[其他解释]
感谢分享
[其他解释]
支持你,加油
[其他解释]
支持一下,楼主,加油!同时祝楼主中秋国庆节快乐!
[其他解释]
感谢分享
------其他解决方案--------------------


引用:
不知道支持unicode不,如果只是支持ascii,那开源的已经很多了
引用:
支持简体汉字、繁体汉字、韩文、……吗?(^_^)
只要用utf8,都支持~~^_^
因为w3c说.xml唯一支持的字符编码就是utf8.所以其它的扩展都被我自动忽略了.

开源的xml解析器是不少,但毛病也不少.性能大多也比较差,灵活性也不够.
我写的这个主要有以下的特点:
1.解析性能高.用非in place的解析方式达到了in place方式性能.只有微小差别.
2.可适配的读写策略,可以灵活的和其它代码融合.
3.XML容器和XML分析器分离.它们可以组合使用,也可以独立使用.比如,我在另一个项目里,仅数十行代码就实现了一个简易的XML适配器,从而复用XML解析器实现XML的SAX解析器.也可以实现其它的分析器,进行XML容器复用.原本我还设计了一套二进制的格式来描述XML的...
4.通过标准的内存分配器接口,实现可定制的内存分配.便于应用在对内存有特殊要求的场合.
5.原生C++接口,使用简单.采用了类似STL容器的,可迭代的接口.符合C++的风格和习惯.很容易在这个基础上做其它的扩展,比如XPath.多数的解析器都是照搬DOM接口,但那个东西发挥不出C++的优势.
6.只有一个头文件,无需配置直接使用.而且foot print很小,编译后只有几十k大.
基本上,当时设计的目标都达到了.只有一点,就是很多非主流编译器不兼容...
[其他解释]
上图来自:来自:http://rapidxml.sourceforge.net/manual.html
[其他解释]
楼主,效率如何啊!贴张常用开源xml库的效率比较图吧:


[其他解释]
就性能来说,肯定是比rapidxml和pugixml慢的.毕竟它们就不分配内存,把原始的内存给改了.
就我以前测试的结果来说,dom方式解析,是和pugxml差不多还要快一点的.而sax方式解析,还可以再快一倍.但毕竟我不能直接把xml原始数据给改了.为了提高性能,我甚至连字符串类都自己实现了...
[其他解释]
感谢分享,顺便接分。
[其他解释]
mark.
[其他解释]
不错 不错
[其他解释]
楼主V5,我写的还都揣着呢,没脸拿出手.呵呵.
[其他解释]
当时设计的时候,为了加速属性的访问,用了个MAP,这玩意不是那么必要.毕竟一个节点的属性不会太多.使用频度也没有大到要用MAP加速的地步.

本着对大家负责的原则,我会抽空把这部分设计改进一下.估计还能把性能提升一些.同时,也会顺手改进一下程序结构,减少一些编译器兼容性问题.
[其他解释]
该回复于2012-09-28 16:31:05被版主删除
[其他解释]
感谢分享,顺便接分。
[其他解释]
引用:
不错, 不过还是跟喜欢自动生成代码的, 曾经打算写一个解析类似描述, 自动生成C代码的工具,
后来只写了个半成品, 然后就很少用到xml了, 结果就没完成.

document [ name("root") , property {
[option , defval( NULL ) , name("sprop")] String sPropRoot;
……
YACC之流在算法上的确是可以做到相当的优化了.但问题是它的实现的确不怎么优化.手写解析器超越它比较容易.以前玩这个的时候,还用它实现过一个脚本.
自动生成代码.生成的好了还好,生成的比较烂的话,维护可是个麻烦....甚至一度我宁愿手工做状态机,写状态表.
最近做个东西,用了GSOAP...直接生成几个20M的源文件.搞的LD都要报符号太多...

如果你的代码生成器能做到输入需求,输出应用程序....那就完美了^_^
[其他解释]
支持开源。谢谢楼主。
[其他解释]
不错啊,支持中文么?虽然做到也很容易,但是感觉支持了比较好,前两天用了别人的库,所有中文部分都是自己转的,好麻烦
[其他解释]
不错 马上copy下来。。。学习了

[其他解释]
感谢分享,加油
[其他解释]
厉害。。。。。。。。。。。。。。。。。。。
[其他解释]
太好了,支持楼主。

我能开放的代码不多啊,只有不同版本的hello world啊。


[其他解释]

引用:
太好了,支持楼主。

我能开放的代码不多啊,只有不同版本的hello world啊。

[其他解释]
引用:
不错啊,支持中文么?虽然做到也很容易,但是感觉支持了比较好,前两天用了别人的库,所有中文部分都是自己转的,好麻烦
要看你支持到什么程度了....
如果只是mbs的中文,比如gb2312,utf8之类的,那肯定是没问题的.但你要原生支持utf16之类的.那只有改一下了...

刚才稍微改进了一下性能.性能提升了30%.现在剩下的主要就是内存分配用的时间了.再想想看有什么好办法可以在不用in place分析策略的情况下,再提升一下性能.
[其他解释]
引用:
太好了,支持楼主。

我能开放的代码不多啊,只有不同版本的hello world啊。


呵呵,老大,写5个版本的hello world出来让我们开开眼界啊!
[其他解释]
感谢分享!!
[其他解释]
感谢分享,支持楼主。
[其他解释]
duoxie!!!!!!
[其他解释]
比expat如何?它只支持sax,据说非常快,我也用过几次,感觉接口不错。
[其他解释]
果断收藏,抽空测试一下运行效率,楼主辛苦了
[其他解释]
hao
[其他解释]
支持下
[其他解释]
还是支持下 真的不错额
[其他解释]
引用:
引用:不错啊,支持中文么?虽然做到也很容易,但是感觉支持了比较好,前两天用了别人的库,所有中文部分都是自己转的,好麻烦要看你支持到什么程度了....
如果只是mbs的中文,比如gb2312,utf8之类的,那肯定是没问题的.但你要原生支持utf16之类的.那只有改一下了...

刚才稍微改进了一下性能.性能提升了30%.现在剩下的主要就是内存分配用的时间了.再想想看有什么好……

有机会也试试
[其他解释]
这种基础功能从来不自己写的人路过必须顶一下
[其他解释]
谢谢分享!
[其他解释]
支持一下,多多分享。

[其他解释]
不大懂,有机在尝试下吧。
[其他解释]
又做了点小改进,最后还是用了一直不想用的chunk分配器.性能有所提高.
对比测试:
pugiXML 最新版,应该比RapidXML快.至少比它自己以前的版本快了30%多
平均 7.8 指令每字符

我的XML
平均 19.9 指令每字符

以上用gcc 4.7.2测试.
不知道VC11有什么问题,同样的代码,编译出来要比GCC慢一倍...
再研究一下.过几天发个最终版.不用in place策略,性能实在难提升啊
[其他解释]
来学习了
[其他解释]
xpath支持下多好
[其他解释]
好贴必须要顶
[其他解释]
貌似不错,感谢分享 好贴必须要顶

[其他解释]
感谢分享。。。
[其他解释]
做过一个xml<--->treeview互转的工具
不过实在不喜欢xml:
解析开销太大,还是喜欢key=value的ini扩展到支持层次信息
------其他解决方案--------------------


大牛 不错 学习了啊啊啊
[其他解释]
谢谢分享,写代码真是一件细心费力的事
[其他解释]
楼主写的很好啊
[其他解释]
大神......
[其他解释]
tinyxml现成的开源库很多
[其他解释]
该回复于2012-09-30 06:06:02被版主删除
[其他解释]
楼主,支持一下!!!

[其他解释]
真心崇拜开源牛人
[其他解释]
支持一下~
[其他解释]
该回复于2012-10-02 14:49:24被版主删除
[其他解释]
老兄 您太厉害啦
[其他解释]
再次做了些许改进.去掉了一点不必要的兼容性判断.改进了一些代码的写法.

最新版地址. http://www.kuaipan.cn/file/id_12470514853353275.htm

这个版本做了点简单的测试.


我的XML pugiXML tinyxml2 所有字符求和
gcc 4.7.2: 16.5/14.9 6.9 26.8 2.2
vc11: 26.1/19.8 6.2 25.2 1.9
vc10: 25.8/19.7 6.3 26.7 1.8
icl13: 20.5/17.5 5.9 24.5 2.2
数据单位为:分析一个字符所用的平均指使周期

pugiXML是整块in place方式解析(即原地解析完整的XML,直接把传入的数据修改掉,这样可以少分配很多内存),它的解析基本上是通过宏,手工实现全内联.所以它的性能最好.
tinyxml2是做的整块解析(即XML字符串进行完全预读,全都加载进内存), 它比tinyxml1快了很多.
我的xml是用流方式解析,一次只从流中读取一个字符.可以由用户自定义读取接口.适配起来有很大的灵活性.但这种方式在性能上有比较大的牺牲.尤其是编译优化不到位的时候,会比较慢.但总的来说,比tinyxml2要快一点.
而我的第二组数据是测试时写的整块解析器的结果(还未做调整,有望改成流方式,未发布).这种方式,编译优化比较到位.性能会快一些.
总的来说.我在设计的时候,希望能保持输入,输出接口的灵活性.又不希望进行整体预读(减少峰值内存占用).还不希望做原地解析(对原数据有破坏性,虽然可以通过副本避免,但那要增倍内存占用).暂时只能做到这样.如果做完整的手工内联,也许在vc和icl上会快一些.不过维护起来就费事了.
不过,XML本身就不是很适合那些高强度的应用点.所以,目前来说,上面用到的解析器都足够快了.在实际应用中不会有太大的差距.
[其他解释]
膜拜牛人们。。。
[其他解释]
不错啊,都是牛擦人
[其他解释]
不错!!!!!!!!!!!!!!!
[其他解释]
不错,好好学习了,感谢楼主了
[其他解释]
正好我与偶看到撒娇。
[其他解释]
我以前用过Markup
[其他解释]
感谢分享
[其他解释]
不错学习了,很好。
[其他解释]
感谢楼主分享,收藏了!

------其他解决方案--------------------


支持不容易啊
[其他解释]
null
[其他解释]
正好我与偶看到撒娇
[其他解释]
支持一下,多多分享
[其他解释]
自己先顶~~~
另外:
其中有vs调试器的可视化脚本.用vs的同志可以在调试时直接看到xml的结构和内容.
用法自己上网找..关键字:autoexp.dat
[其他解释]
该回复于2012-09-28 08:38:48被版主删除
[其他解释]
该回复于2012-09-28 08:39:06被版主删除
[其他解释]
该回复于2012-09-28 09:17:51被版主删除
[其他解释]
该回复于2012-09-28 09:54:37被版主删除
[其他解释]
该回复于2012-09-28 10:56:14被版主删除
[其他解释]
该回复于2012-09-28 11:28:22被版主删除
[其他解释]
很给力,拜服了。
[其他解释]
开源的很多效率真的很低
[其他解释]
该回复于2012-09-28 13:40:21被版主删除
[其他解释]
不错, 不过还是跟喜欢自动生成代码的, 曾经打算写一个解析类似描述, 自动生成C代码的工具,
后来只写了个半成品, 然后就很少用到xml了, 结果就没完成.

document [ name("root") , property {
[option , defval( NULL ) , name("sprop")] String sPropRoot;
[option , defval( -1 ) , name("iprop") ] int32 iPropRoot; } ] xmlfoobar
{
[name("description")]String sDescription;
[name("version")] int32 iVersion ;

struct contents
{
list of struct content
{
String content_1;
int32content_2;
float content_3;
}
}
}

可以自动的解析类似的xml文件:
<root [sprop="option string property value"] [iprop="100"]>
<description> description value </description>
<version> 1234 </version>
<contents>
<content>
<content_1> String value </content_1>
<content_2> 1234 </content_2>
<content_3> 12.34 </content_3>
</content>
<content>
<content_1> String value 2</content_1>
<content_2> 12345 </content_2>
<content_3> 12.345 </content_3>
</content>
</contents>
</root>

自动的生成类似的C头文件和解析的函数(用yacc做的下推自动机, 方便边下载, 边解析)

struct xmlfoobar
{
String sPropRoot;
int32 iPropRoot ;

String sDescription;
int32 iVersion ;



struct xmlfoobar_contents
{
LIST_ENTRYcontent;
};
};

struct xmlfoobar_contents_content
{
LIST_NODE ;

String content_1;
int32content_2;
float content_3;
};

啥时候有空了看能做完不..
[其他解释]
严重关注
[其他解释]
不错!先做个标记。
[其他解释]
先来下载地址
[其他解释]
小白我没看懂......
[其他解释]
围观,强烈围观哈哈哈啊
[其他解释]
牛人啊啊
[其他解释]
貌似还不错,楼主有才了。

读书人网 >C++

热点推荐