XML格式使用越来越广泛,不论是游戏还是普通应用软件都有使用。而我喜欢用xml作为配置文件。XML解析的方法实在很多,最根本的两种是SAX和DOM。SAX需要一边读取文件一边解析,速度较快;而DOM采用树状结构保存解析文件,使用方便。iPone自带的XML解析器一则无法跨平台,一则使用不便,还有一些bug(我是指SDK 2.0,新版本SDK 3.0的XML解析器没用过,所以没有发言权),所以我决定使用其他开源XML解析器。开源的XML解析器也有很多种,比如libxml2,TouchXML(iPhone版),tinyxml等。其中tinyxml简单易用,如果你不要求诸如DTD等功能,那么tinyxml绝对应该是你的首选。我采用的是ticpp(tinyxml的cpp版本,采用DOM方法)。
项目下载
下面是ticpp的Xcode项目下载。
(注:虽然我讨论的是跨平台代码,但我这里只是提供了Xcode项目,要在Windows下使用也是十分简单的事情,你可以自创Visual Studio项目文件或使用GNU make)
使用方法
这里我给出一个xml文件示例:
1 2 3 4 5 6 | <options version="1.0"> <levels> <level name="InTheBeginning" enemy="10" /> <level name="Fighting" enemy="100" /> </levels> </options> |
首先,我定义了一个XML解析器类,它定义了XML解析的接口,我使用ticpp,但是如果你更改XML解析器为比如libxml2,你只需更改类的实现部分,你运用XML解析器的程序代码仍然可以保持不变。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 | //////////////////////// 头文件 /////////////////////////////////// /* * xmlparser.h * utils * * Created by Bagusflyer on 09-6-27. * Copyright 2009 www.iphone-geek.cn. All rights reserved. * */ #ifndef _UTILS_XMLPARSER_H_ #define _UTILS_XMLPARSER_H_ #include <fstream> #include "ticpp/ticpp.h" typedef ticpp::Element XmlNode; typedef ticpp::Document XmlDocument; using namspace std; namespace utils { class CXmlParser { public: CXmlParser():m_pRootNode(NULL) {} void parse(const string& file,const string& rootName); void parse(ifstream& stream,const string& rootName); void parse(const char* buf, size_t len, const string& rootNode); int getAttribute(XmlNode& elem, const string& attrib, int defval); float getAttributeFloat(XmlNode& elem, const string& attrib, float defval); string getAttribute(XmlNode& elem, const string& attrib); string getText(XmlNode& elem); string getName(XmlNode& elem); XmlNode* getFirstChild(XmlNode& elem,const string& nodeName); XmlNode* getFirstChild(XmlNode& elem); XmlNode* getNextChild(XmlNode& elem,string nodeName=""); XmlNode* rootNode() const { return m_pRootNode; } private: XmlDocument m_doc; XmlNode* m_pRootNode; }; } #endif // _UTILS_XMLPARSER_H_ //////////////////////////// 实现文件 /////////////////////////////// /* * xmlparser.cpp * utils * * Created by Bagusflyer on 09-6-27. * Copyright 2009 www.iphone-geek.cn. All rights reserved. * */ #include "utils/xmlparser.h" namespace utils { int CXmlParser::getAttribute(XmlNode& elem,const string& attrib,int defval) { long value; elem.GetAttributeOrDefault(attrib,&value,defval); return value; } float CXmlParser::getAttributeFloat(XmlNode& elem, const string& attrib, float defval) { float value; elem.GetAttributeOrDefault(attrib,&value,defval); return value; } string CXmlParser::getAttribute(XmlNode& elem,const string& attrib) { string value; elem.GetAttributeOrDefault(attrib,&value,_T("")); return value; } void CXmlParser::parse(const string& file,const string& rootName) { std::ifstream in(file.c_str()); if ( !in.good() ) { // 注意:错误检查,略去 //throw XmlParserError(_T("Can't open "+file)); } parse(in,rootName); } void CXmlParser::parse(ifstream& stream,const string& rootName) { stream.seekg(0,std::ios::end); size_t len = stream.tellg(); stream.seekg(0,std::ios::beg); char* buf = new char[len]; stream.read(buf,len); try { parse((const char*)buf,len,rootName); delete [] buf; } catch (ticpp::Exception& ex) { delete [] buf; throw ex; } } void CXmlParser::parse(const char* buf, size_t len, const string& rootNode) { try { // Load a document std::string data(buf,len); m_doc.Parse(data); m_pRootNode = m_doc.FirstChildElement(rootNode); } catch( ticpp::Exception& ex ) { // If any function has an error, execution will enter here. // Report the error // 注意:请使用自己的XmlParser exception类 // throw XmlParserError("Failed to parse xml"); } } string CXmlParser::getText(XmlNode& elem) { return elem.GetTextOrDefault(_T("")); } string CXmlParser::getName(XmlNode& elem) { return elem.Value(); } XmlNode* CXmlParser::getFirstChild(XmlNode& elem,const string& nodeName) { return elem.FirstChildElement(nodeName,false); } XmlNode* CXmlParser::getFirstChild(XmlNode& elem) { return elem.FirstChildElement(false); } XmlNode* CXmlParser::getNextChild(XmlNode& elem,string nodeName) { if ( nodeName.empty() ) return elem.NextSiblingElement(false); else return elem.NextSiblingElement(nodeName,false); } } // namespace utils |
然后,在你的代码(比如我有一个COptionParser,它包含了一个utils::CXmlParser对象m_xmlPaser)中使用XmlParser进行解析:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | /////////////////// 以下是进行解析的代码 ////////////////////////////// void parser(string& xmlFile) { m_xmlParser.parse(xmlFile,"options"); // 解析version属性 string version = m_xmlParser.getAttribute(*(m_xmlParser.rootNode()),"version"); XmlNode* pLevelsNode = m_xmlParser.getFirstChild(*m_xmlParser.rootNode(),"levels"); if ( !pLevelsNode ) { // 定义自己的exception //throw utils::XmlParserError("No levels found"); if (pLevelsNode ) { // 解析每个Level XmlNode* chd = m_xmlParser.getFirstChild(*pLevelsNode ,"level"); while (chd) { // 解析level名称 string name = m_xmlParser.getAttribute(*chd,"name"); // 解析敌人数目,缺省值为15 int enemies = m_xmlParser.getAttribute(elem,"enemy",15); // 下一个level chd = m_xmlParser.getNextChild(*chd,"level"); } } } } |
代码虽然不少,可是思路还是很简单的。下一次,我会介绍跨平台压缩库。有什么问题,欢迎留言探讨!



