因为项目需要读取xml配置文件,在原来调查一番后,项目组使用了tinyxml.
tinyxml确实简单,非常清楚的就把读取方案写出来了。但是,由于后期xml文件越来越大(2.5M,大概1w多行数据),结果导致运行速度越来越低(17s)。
于是,不得不开始寻找改善方案。
在网上调查一番后,普遍认为xml读取有以下的几种方式:
RapidXml、pugixml 0.3、pugxml、TinyXml
并且清楚的给出了各个之间的性能对比。
strlen() | RapidXml | pugixml 0.3 | pugxml | TinyXml | ||
---|---|---|---|---|---|---|
抱着试一试的态度,我采用了rapidxml来进行改善,结果运行速度减少到了1.6S!
下面是rapidxml的一些使用注意事项,总结一下,帮助大家少走弯路。
1.大家网上百度的时候,肯定都会存在这样的代码:
1 file<> fdoc("config.xml"); 2 std::cout<<fdoc.data()<<std::endl; 3 xml_document<> doc; 4 doc.parse<parse_full>(fdoc.data());
代码本身是没有错的,但是导入工程后,就会在file<> fdoc抛出异常。
调查许久后发现:file<> fdoc后面必须是绝对路径,所以我们读取的时候不必要像上面那么书写,可以这样:
1 using namespace rapidxml; 2 std::string strXml = m_strXmlPath; // m_strXmlPath是获取xml路径的方法,可以自己去实现 3 strXml.append("\\CA_Basic.xml"); 4 file<> fdoc(strXml.c_str()); 5 xml_document<> doc; 6 doc.parse<0>(fdoc.data());
2.网上的xml的格式都是很整齐的,比如:
1 <config> 2 <color> 3 <red>0.1</red> 4 <green>0.1</green> 5 <blue>0.1</blue> 6 <alpha>1.0</alpha> 7 </color> 8 <size> 9 <x>640</x> 10 <y>480</y> 11 <w>0</w> 12 <h>0</h> 13 </size> 14 <mode fullscreen="false">screen mode</mode> 15 </config>
这样读取起来确实和简单,按照一下的方案就行了。
1 //! 获取根节点 2 xml_node<>* root = doc.first_node(); 3 4 //! 获取根节点第一个节点 5 xml_node<>* node1 = root->first_node(); 6 xml_node<>* node11 = node1->first_node(); 7 ......
可是实际运用的时候不会这么简单的,比如我们有以下的xml:
1 <ca_table> 2 <ce_port no="0"> 3 <ca item_id ="10004000" curr_ec="1" next_ec="2"> 4 <firm no ="0" vv ="10" ll ="40" pp ="0000"> </firm> 5 <firm no ="1" vv ="11" ll ="40" pp ="0000"> </firm> 6 <board_hard pn ="CA07111-C631" sn ="PP09280285" rev ="40" other =""> </board_hard> 7 <port port_no ="0" item_id ="11004000" rate ="0" node_name="500000E0D1000100" wwn="500000E0D1000100" MacAddress="B0ACFAA3A000" lu_reset_scope="01" reserveCancel="00"> 8 </port> 9 </ca> 10 </ce_port> 11 </ca_table>
这个时候就比较麻烦了,我们可以这样去读取:
1 using namespace rapidxml; 2 std::string strXml = m_strXmlPath; 3 strXml.append("\\CA_Basic.xml"); 4 file<> fdoc(strXml.c_str()); 5 xml_document<> doc; 6 doc.parse<0>(fdoc.data()); 7 8 xml_node<>* root = doc.first_node(); // 获取根节点<ca_table> 9 xml_node<>* pCePortNodeBasic = root->first_node("ce_port"); // 获取节点<ce_port> 10 if (!pCePortNodeBasic) { 11 return; 12 } 13 xml_node<>* pCaBasic = pCePortNodeBasic->first_node("ca"); // 获取节点<ca> 14 xml_attribute<>* p = pCaBasic ->first_attribute(“item_id”); // 获取节点<ca>中的itemid 15 unsigned long long value = strtol(p->value(), NULL, 16); // p->value()获取的是字符,需要将其转换为数值类型
3.并列的xml怎么处理?
跟tinyxml一样,rapidxml也自己封装了搜寻下一个并列的xml的函数next_sibling(),直接调用即可。
4.命名空间rapidxml
在使用rapidxml的时候,尽可能的在调用rapidxml的地方再使用rapidxml命名空间,最好不要放到全局,以免引起混淆和带来性能问题