`
ladymaidu
  • 浏览: 680976 次
文章分类
社区版块
存档分类
最新评论

C++中的XML配置文件编程经验

 
阅读更多

来源:http://www.blogjava.net/wxb_nudt/archive/2008/05/27/203317.html<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

C++中并没有操作XML文件的标准库,因此大家需要使用各自熟悉的XML库来解决XML文件的读取与写入。XML的一个重要用途是作为程序的配置文件,存储程序运行相关的各种数据。本文总结了使用libxml2库来对XML配置文件进行编程的一些经验。最后提供了一个封装好的类CXMLConfig,并详细说明了该类的功能、使用方法和注意事项。

阅读本文所需的技术背景:

l C/C++简单语法;

l XML技术,XPATH技术;

l C++编译器知识;

本文的内容包括:

l 下载与安装LIBXML2ICONV

l 第一个例子程序的编写、编译链接和运行;

l 使用XPATH读出多个配置项的值;

l XML的配置文件类CXMLConfig

l 将配置项写入XML文件;

l CXMLConfig类使用小结;

阅读本文之前最好先读我的上一篇博客C++的XML编程经验――LIBXML2库使用指南,那一篇专门介绍libxml2库的使用方法。本文将不会再详细介绍libxml2的使用,而是集中精力介绍如何存取XML中的数据。

本文的源代码是一个VC6的工程,里面包含三个子工程。地址在http://www.blogjava.net/Files/wxb_nudt/XMLConfigFile.rar

1. 下载与安装LIBXML2ICONV

为了方便读者,这一段原文照抄上一篇博客。

Libxml2是一个C语言的XML程序库,可以简单方便的提供对XML文档的各种操作,并且支持XPATH查询,以及部分的支持XSLT转换等功能。Libxml2的下载地址是http://xmlsoft.org/,完全版的库是开源的,并且带有例子程序和说明文档。最好将这个库先下载下来,因为这样可以查看其中的文档和例子。

windows版本的的下载地址是http://www.zlatkovic.com/libxml.en.html;这个版本只提供了头文件、库文件和dll,不包含源代码、例子程序和文档。在文本中,只需要下载libxml2库、iconv库和zlib库就行了(注意,libxml2库依赖iconvzlib库,本文中重点关注libxml2iconvzlib不介绍),我使用的版本是libxml2-<?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" /><chsdate w:st="on" isrocdate="False" islunardate="False" day="30" month="12" year="1899">2.6.30</chsdate>.win32.zipzlib-1.2.3.win32.zipiconv-1.9.2.win32.zip

在编程的时候,我们使用windows版本的libxml2zlibiconv,将其解压缩到指定文件夹,例如D:"libxml2-<chsdate w:st="on" isrocdate="False" islunardate="False" day="30" month="12" year="1899">2.6.30</chsdate>.win32D:"zlib-1.2.3.win32以及D:"iconv-1.9.2.win32。事实上,我们知道在windows下面使用头文件、库文件和dll是不需要安装的,它又没有使用任何需要注册的组件或者数据库,只需要告诉编译器和链接器这些资源的位置就可以了。

注意:要在path变量中加上D:"iconv-<chsdate w:st="on" isrocdate="False" islunardate="False" day="30" month="12" year="1899">1.9.2</chsdate>.win32"bin;D:"zlib-1.2.3.win32"bin;D:"libxml2-2.6.30.win32"bin这三个地址,否则在执行的时候就找不到。或者使用更简单的方法,把其中的三个dll到拷贝到system32目录中。

有两种方法来编译链接基于libxml2的程序,第一种是在VC环境中设置libinclude路径,并在link设置中添加libxml2.libiconv.lib;第二种是用编译器选项告诉编译器cl.exe头文件的位置,并用链接器选项告诉链接器link.exe库文件的位置,同时在windows环境变量path中添加libxml2bin文件夹的位置,以便于程序运行时可以找到dll(也可以将dll拷贝到system32目录下)。

2. HELLO,XML CONFIG FILE

本节的源代码位于项目HelloXml中,使用的xml文件是Helloxml.xml

在安装配置好libxml2iconv库之后,就可以写一个简单的程序来读取XML中的数据了。该XML内容如下:

<?xml version="1.0" encoding="GB2312" ?>

<main>20080526</main>

使用libxml2库读取main节点包含的内容,代码如下:

  1. xmlChar*LoadConfigFile(constchar*szConfigFilename,xmlChar*xszRel)
  2. {
  3. xmlDocPtrdoc;//定义解析文档指针
  4. xmlNodePtrcurNodePtr;//定义结点指针
  5. doc=xmlReadFile(szConfigFilename,"GB2312",XML_PARSE_RECOVER);//解析文件
  6. if(doc==NULL)
  7. {
  8. fprintf(stderr,"Documentnotparsedsuccessfully."n");
  9. xmlFreeDoc(doc);
  10. exit(1);
  11. }
  12. curNodePtr=xmlDocGetRootElement(doc);//确定文档根元素
  13. /*检查确认当前文档中包含内容*/
  14. if(curNodePtr==NULL)
  15. {
  16. fprintf(stderr,"emptydocument"n");
  17. xmlFreeDoc(doc);
  18. exit(1);
  19. }
  20. //读取xml文档中的内容并赋值给对象属性
  21. xszRel=xmlNodeGetContent(curNodePtr);
  22. xmlFreeDoc(doc);
  23. returnxszRel;
  24. }
  25. intmain(intargc,char*argv[])
  26. {
  27. xmlChar*xszContent=NULL;
  28. xszContent=LoadConfigFile("..""Debug""HelloXml.xml",xszContent);
  29. if(xszContent!=NULL)
  30. {
  31. cout<<"HELLO,XMLCONFIGFILE.content="<<xszContent<<endl;
  32. xmlFree(xszContent);
  33. }
  34. return0;
  35. }

编译代码之前要注意:xml文档存放的地点不是本项目文件夹,而是项目文件夹上层的Debug目录,同时将编译和链接的目的文件夹都设置为项目文件夹上层的Debug目录。第二点,在link选项中加入了libxml2.libiconv.lib。第三点,在系统的Path变量中指明了libxml2.dlliconv.dllzlib1.dll的路径(为了方便读者,我将这三个dll都拷贝到了Debug目录下面)。

编译链接完毕后运行程序,得到如下结果:

HELLO, XML CONFIG FILE. content = 20080526

3. 使用XPATH读出多个配置项的值

本节的源代码位于项目XPathConfig中,使用的xml文件是XPathConfig.xml

上面的例子中,为了理解的便利仅在根节点中存储了一个值,而实际的配置文件往往是同时存放多个配置项的值。举例如下:

<main>

<IP>127.0.0.1</IP>

<Port>80</Port>

</main>

Xml中存储了一个IP地址和一个端口值。其XPATH地址分别是/main/IP//main/Port/。当然,更加复杂的XPATH值也可同样处理。

为了方便的操作xml文档,我写了一组xml函数,位于Code_Conv.hCode_Conv.cpp中,其功能如下:

l openXmlFile,打开Xml文档,返回文档指针;

l closeXmlFile,关闭Xml文档;

l getXmlString,根据XPATH路径读取字符串;

l getXmlInt,根据XPATH路径读取整型值;

为了处理中文以及查询Xpath节点,我还写了四个被上述函数调用的函数:

l code_convert,从一种编码转为另一种编码;

l u<chmetcnv w:st="on" tcsc="0" numbertype="1" negative="False" hasspace="False" sourcevalue="2" unitname="g">2g</chmetcnv>,从UTF-8转换为GB2312编码;

l g2u,从GB2312转换为UTF-8编码;

l get_nodeset,调用xpath查询节点集合,成功则返回xpath的对象指针,失败返回NULL

然后,主程序便简化为:

  1. intmain(intargc,char*argv[])
  2. {
  3. xmlDocPtrdoc=openXmlFile("..""Debug""XPathConfig.xml");
  4. stringstrIP=getXmlString(doc,"/main/IP");
  5. intiPort=getXmlInt(doc,"/main/Port");
  6. cout<<"IP="<<strIP.c_str()<<"Port="<<iPort<<endl;
  7. closeXmlFile(doc);
  8. return0;
  9. }

运行结果为:

IP = <place w:st="on"><placename w:st="on">127.0.0.1</placename><placetype w:st="on">Port</placetype></place> = 80

观察上面的代码可以发现,整个主程序几乎与libxml2库无关了,除了一个xmlDocPtr变量。再次观察可以发现,这个变量几乎出现在每个自定义函数中,它代表的是一种状态,或者可以称为属性。而那些自定义函数可以称之为功能。因此,按照许多C++专著的说法,属性+功能=对象。《C++沉思录》中说道,CC++最大的不同在于,C++拥有一个最合适的存储程序状态的位置,即对象的属性;而C则必须在许多函数中留出一个位置来保存这个状态。这句话,简直正确得可怕

4. XML的配置文件类CXMLConfig

本节的源代码位于项目UseClass中,使用的xml文件还是XPathConfig.xml

于是有了下面的CXMLConfig类定义:

  1. classCXMLConfig
  2. {
  3. public:
  4. CXMLConfig(constchar*szXmlFilename);
  5. ~CXMLConfig();
  6. //根据XPATH路径读取字符串
  7. stringgetXmlString(constchar*szXpath);
  8. intgetXmlInt(constchar*szXpath);
  9. private:
  10. //代码转换:从一种编码转为另一种编码
  11. intcode_convert(char*from_charset,char*to_charset,char*inbuf,intinlen,char*outbuf,intoutlen);
  12. //UNICODE码转为GB2312码
  13. //成功则返回一个动态分配的char*变量,需要在使用完毕后手动free,失败返回NULL
  14. char*u2g(char*inbuf);
  15. //GB2312码转为UNICODE码
  16. //成功则返回一个动态分配的char*变量,需要在使用完毕后手动free,失败返回NULL
  17. char*g2u(char*inbuf);
  18. //调用xpath查询节点集合,成功则返回xpath的对象指针,失败返回NULL
  19. xmlXPathObjectPtrget_nodeset(constxmlChar*xpath);
  20. private:
  21. stringm_strFilename;
  22. xmlDocPtrm_doc;
  23. };

使用这个类来改写主程序,可以让使用者完全脱离libxml2的库环境,并且省略了打开和关闭xml文件的步骤,因为这些工作在构造和析构函数中完成了。

  1. intmain(intargc,char*argv[])
  2. {
  3. CXMLConfigxmlConfig("..""Debug""XPathConfig.xml");
  4. stringstrIP=xmlConfig.getXmlString("/main/IP");
  5. intiPort=xmlConfig.getXmlInt("/main/Port");
  6. cout<<"IP="<<strIP.c_str()<<"Port="<<iPort<<endl;
  7. return0;
  8. }

运行结果为:

IP = <place w:st="on"><placename w:st="on">127.0.0.1</placename><placetype w:st="on">Port</placetype></place> = 80

5. 将配置项写入XML文件

本节的源代码位于项目UseClass中,使用的xml文件依然是XPathConfig.xml

目前CXMLConfig类已经有了打开xml文件,读取数据以及关闭xml文件的功能。还缺少写入数据的功能。写入数据功能的算法也很简单:先将xml文件读入内存,然后通过xpath找到相应节点,并修改节点内容,最后将内存中的xml文件一次性写入硬盘。这里有一点要注意,如果在写入过程中硬盘断电或者出现其他故障,则会造成无法恢复的错误,数据会全部丢失。为了防止这种情况,还应该在写入前进行数据备份的工作。通盘考虑后,在CXMLConfig类中加入如下函数:

writeXmlString:将字符串写入xml文档相应节点;

writeXmlInt:将整型写入xml文档相应节点;

saveConfigFile:将内存中的xml文档写入硬盘;

saveBakConfigFile:保存当前的xml文档到bak文件(即xml文档名加_BAK.XML)中;

loadBakConfigFile:将bak文件读入内存;

注意,在调用saveConfigFile时会自动调用saveBakConfigFile,将原有配置文件保存为备份文件。修改后的类如下:

  1. classCXMLConfig
  2. {
  3. public:
  4. CXMLConfig(constchar*szXmlFilename);
  5. ~CXMLConfig();
  6. //根据XPATH路径读取字符串
  7. stringgetXmlString(constchar*szXpath);
  8. intgetXmlInt(constchar*szXpath);
  9. boolwriteXmlString(conststringstrValue,constchar*szXpath);
  10. boolwriteXmlInt(constintiValue,constchar*szXpath);
  11. boolsaveConfigFile();
  12. boolsaveBakConfigFile();
  13. boolloadBakConfigFile();
  14. private:
  15. //代码转换:从一种编码转为另一种编码
  16. intcode_convert(char*from_charset,char*to_charset,char*inbuf,
  17. intinlen,char*outbuf,intoutlen);
  18. //UNICODE码转为GB2312码
  19. char*u2g(char*inbuf);
  20. //GB2312码转为UNICODE码
  21. char*g2u(char*inbuf);
  22. //调用xpath查询节点集合,成功则返回xpath的对象指针,失败返回NULL
  23. xmlXPathObjectPtrget_nodeset(constxmlChar*xpath);
  24. //禁止拷贝构造函数和"="操作
  25. CXMLConfig(constCXMLConfig&);
  26. CXMLConfig&operator=(constCXMLConfig&);
  27. private:
  28. stringm_strFilename;
  29. xmlDocPtrm_doc;
  30. };

然后我们修改了主程序,其功能为读出数据后修改了数据,然后存入了配置文件,主程序如下:

  1. intmain(intargc,char*argv[])
  2. {
  3. CXMLConfigxmlConfig("..""Debug""XPathConfig.xml");
  4. stringstrIP=xmlConfig.getXmlString("/main/IP");
  5. intiPort=xmlConfig.getXmlInt("/main/Port");
  6. cout<<"IP="<<strIP.c_str()<<"Port="<<iPort<<endl;
  7. strIP="127.1.1.1";
  8. iPort=81;
  9. xmlConfig.writeXmlString(strIP,"/main/IP");
  10. xmlConfig.writeXmlInt(iPort,"/main/Port");
  11. if(xmlConfig.saveConfigFile())
  12. {
  13. cout<<"SaveConfigfilesuccess!"<<endl;
  14. }
  15. return0;
  16. }

运行完以后会发现两个结果,第一个是配置文件XPathConfig.xml中的内容已经被修改,第二个是原配置文件内容备份在XPathConfig_bak.xml中。

6. CXMLConfig类使用小结

目前为止,CXMLConfig类提供了较为便利的读取和保存XML配置文件的功能。那么使用CXMLConfig需要哪些步骤呢?

第一,正确安装了libxml2iconv库,包括头文件、lib文件和dll文件。注意头文件主要是libxml2iconv的头文件,lib文件就是两个libxml2.libiconv.lib,而dll有三个,即libxml2.dlliconv.dllzlib1.dll注意:如果你没有正确安装,那么无法正确编译我的例子程序,但是可以运行,因为我已经将dll都包含到运行目录下

第二,确信你弄懂了你的xml配置文件结构,并放在正确的地方;

第三,使用CXMLConfig xmlConfig("..""Debug""XPathConfig.xml")语句正确构造一个CXMLConfig对象,并调用相应的方法来操作xml文件。

CXMLConfig类使用的注意事项:

第一,注意xml文件必须使用节点来存储数据,而不是属性。若使用属性来保存数据,CXMLConfig类不会正确读出其数据,当然更不能正确写入。若有兴趣,可以扩展CXMLConfig类来实现对属性数据的存取,事实上那非常简单。

第二,若有两个节点的XPATH路径相同,例如

<main>

<IP>127.0.0.1</IP>

<IP>127.0.0.2</IP>

<Port>80</Port>

</main>

那么使用getXmlString将只会得到第一个节点的内容。同理,写入时也只会写入第一个节点。

CXMLConfig类的使用环境:

第一, 使用节点来存储数据;

第二, 节点的XPATH路径各不相同;

第三, XML文件最好不大于<chmetcnv w:st="on" tcsc="0" numbertype="1" negative="False" hasspace="False" sourcevalue="100" unitname="m"><span lang="EN-US">100M</span></chmetcnv>。

总之,若有更复杂的要求,请还是仔细研究libxml2或者任意一个开源或商用XML库。

7. 文末的话

事实上,按照原计划这篇博客才刚刚开头,后面才是最精彩的部分。其内容是介绍如何将XML文件当作一个小型的数据库,把多个XPATH路径相同的键和值读入一个std::map<std::string,std::string>中,然后在程序中方便的使用这个map来查找,存取某一类数据。但是由于前面的部分写作时考虑得太详细,而且CXMLConfig类也介绍逐渐趋于完善,因此为了防止喧宾夺主,本文就到这里结束为好。作为一篇libxml2C++的入门文章,恰到好处!

分享到:
评论

相关推荐

    c++读取XML配置文件

    c++读取XML配置文件C++中并没有操作XML文件的标准库,因此大家需要使用各自熟悉的XML库来解决XML文件的读取与写入。XML的一个重要用途是作为程序的配置文件,存储程序运行相关的各种数据。本文总结了使用libxml2库来...

    任意自定义结构体Json配置文件读写

    由于ini原生不支持树结构,需要根据需要将父节点的option作为子节点的section,配置文件可读性变差。xml由于属性和元素分开的形式,不适合直接转为c++定义的树状结构。所以最终选择了json。 要想用c++实现一个通用...

    tinyxml+tinyxpath组合编程套件,很好的编程工具,都是源码

    tinyxpath 解析简单 的小工具,输出是一个静态库。可 找到xml文档. TinyXml介绍 TinyXml是一个基于DOM模型的、非...TinyXml不支持验证,但是体积很小,用在解析格式较为简单的XML文件,比如配置文件时,特别的合适。

    XML编程入门,新手应该需要,无论VC,JAVA

    在这篇文章中,我们尝试了几种选择并且给出了用Perl语言实现的具体解决方案(用Python、Java、C++和其它你喜欢的编程语言与此是一样的)。  我们将使用XML构建一个简单的文本处理程序来存储用户的偏爱设置和其它...

    xerces-c++-3.1.3

    Xerces分析器可处理Java和C++,它采用互联网联盟XML、文件对象模型以及用于XML的简单API标准。所有的Xerces分析器都是模块化可配置的。它为C++版本提供了Perl封装,允许访问Perl的XML分析器,它还提供对统一编码字符...

    C++开源程序库 C++开源程序库

    在文件和目录操作方面,boost也有相应的组件,而在网络编程方面有socket++,还有boost::asio,未来的C ++0X中几乎肯定有一个网络编程和一个线程库。然而目前看来,ACE仍然是进行系统和高性能网络编程的首选,其地位...

    基于Python的编程学习设计源码

    此外,还包括5个MP3音频文件,3个XML配置文件,以及2个SQL数据库文件。这些文件详细展示了如何使用Python、Shell、HTML、JavaScript、C和C++进行编程学习,包括Excel读写追加处理,XML和JSON解析,FLV与MP4转换,...

    基于Python和Django的任务绩效评价系统设计源码

    文件类型包括21个Pyc编译文件、19个Python源代码文件、12个C++源代码文件、10个头文件、10个UI界面文件、10个HTML页面文件、6个XML配置文件、4个JavaScript脚本文件和4个CSS样式文件。该系统通过qt客户端、Django...

    Visual C++程序开发范例宝典(光盘) 第八部分

    实例145 文件复制过程中显示进度条 5.5 文件修改 实例146 更改文件夹图标 实例147 批量删除指定类型的文件 实例148 批量重命名文件 实例149 修改文件属性 实例150 修改文件及目录的名称 5.6 文件的读取与...

    eclipse 开发c/c++

    C 和 C++ 语言都是世界上最流行且使用最普遍的编程语言, 因此 Eclipse 平台(Eclipse Platform)提供对 C/C++ 开发的支持一点都不足为奇。 因为 Eclipse 平台只是用于开发者工具的一个框架,它不直接支持 C/C++;它...

    CPlusPlusXML.rar

    tinyxml 解析xml的例子,如果出现:vc编程中出现 fatal error C1010: 在查找预编译头时遇到意外的文件结尾。是否忘记了向源中添加“#include "stdafx.h"”?解决办法 菜单--〉项目--〉设置,出现“项目设置”对话框,...

    YOLO目标检测+NWPU VHR-10遥感检测数据已标注直接使用(800张图像+对应已标注xml文件+10个类别检测).rar

    2、代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 3、适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。 4、更多仿真源码和数据集下载列表(自行寻找...

    编程新手真言......

    1.16 界面的本质应该是命令行功能支持下的配置描述文件 45 1.17 命令行下编程实践 46 第2章 语言 47 2.1 真正的计算模型 47 2.2 开发模型与语言模型 49 2.3 正规表达式与有限自动机 53 2.4 联系编译原理学语言 56 ...

    vc++ 开发实例源码包

    实例使用了加载类似xml文件读取信息,然后显示。 COM_ATL_Tutorial 简单的atl控件演示 COM接口挂钩及其应用 如题。 CSkinSlier CSliderCtrl自绘 Cursor 生成图标,运行到鼠标图标。 cutscene win32下实现视频...

    Visual C++程序开发范例宝典(PDF扫描版).part3

     cc实例145 文件复制过程中显示进度条   5.5 文件修改   cc实例146 更改文件夹图标   cc实例147 批量删除指定类型的文件   cc实例148 批量重命名文件   cc实例149 修改文件属性   cc实例150 ...

Global site tag (gtag.js) - Google Analytics