来源: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         下载与安装LIBXML2和ICONV;

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.       下载与安装LIBXML2和ICONV

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

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

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

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

注意:要在path变量中加上D:"iconv-1.9.2.win32"bin;D:"zlib-1.2.3.win32"bin;D:"libxml2-2.6.30.win32"bin这三个地址,否则在执行的时候就找不到。或者使用更简单的方法,把其中的三个dll到拷贝到system32目录中。

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

2.       HELLO,XML CONFIG FILE

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

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

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

 <main>20080526</main>

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

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

编译代码之前要注意:xml文档存放的地点不是本项目文件夹,而是项目文件夹上层的Debug目录,同时将编译和链接的目的文件夹都设置为项目文件夹上层的Debug目录。第二点,在link选项中加入了libxml2.lib和iconv.lib。第三点,在系统的Path变量中指明了libxml2.dll、iconv.dll和zlib1.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.h和Code_Conv.cpp中,其功能如下:

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

l         closeXmlFile,关闭Xml文档;

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

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

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

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

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

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

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

然后,主程序便简化为:

  1. int main(int argc, char* argv[])
  2. {
  3. xmlDocPtr doc = openXmlFile("..""Debug""XPathConfig.xml");
  4. string strIP = getXmlString(doc,"/main/IP");
  5. int iPort = getXmlInt(doc,"/main/Port");
  6. cout<<"IP = "<<strIP.c_str()<<" Port = "<<iPort<<endl;
  7. closeXmlFile(doc);
  8. return 0;
  9. }

运行结果为:

IP = 127.0.0.1Port = 80

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

4.       XML的配置文件类CXMLConfig

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

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

  1. class CXMLConfig
  2. {
  3. public:
  4. CXMLConfig(const char* szXmlFilename);
  5. ~CXMLConfig();
  6. //根据XPATH路径读取字符串
  7. string getXmlString(const char *szXpath);
  8. int getXmlInt(const char* szXpath);
  9. private:
  10. //代码转换:从一种编码转为另一种编码
  11. int code_convert(char* from_charset, char* to_charset, char* inbuf,int inlen, char* outbuf, int outlen);
  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. xmlXPathObjectPtr get_nodeset(const xmlChar *xpath);
  20. private:
  21. string m_strFilename;
  22. xmlDocPtr m_doc;
  23. };

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

  1. int main(int argc, char* argv[])
  2. {
  3. CXMLConfig xmlConfig("..""Debug""XPathConfig.xml");
  4. string strIP = xmlConfig.getXmlString("/main/IP");
  5. int iPort = xmlConfig.getXmlInt("/main/Port");
  6. cout<<"IP = "<<strIP.c_str()<<" Port = "<<iPort<<endl;
  7. return 0;
  8. }

运行结果为:

IP = 127.0.0.1Port = 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. class CXMLConfig
  2. {
  3. public:
  4. CXMLConfig(const char* szXmlFilename);
  5. ~CXMLConfig();
  6. //根据XPATH路径读取字符串
  7. string getXmlString(const char *szXpath);
  8. int getXmlInt(const char* szXpath);
  9. bool writeXmlString(const string strValue, const char* szXpath);
  10. bool writeXmlInt(const int iValue, const char* szXpath);
  11. bool saveConfigFile();
  12. bool saveBakConfigFile();
  13. bool loadBakConfigFile();
  14. private:
  15. //代码转换:从一种编码转为另一种编码
  16. int code_convert(char* from_charset, char* to_charset, char* inbuf,
  17. int inlen, char* outbuf, int outlen);
  18. //UNICODE码转为GB2312码
  19. char* u2g(char *inbuf);
  20. //GB2312码转为UNICODE码
  21. char* g2u(char *inbuf);
  22. //调用xpath查询节点集合,成功则返回xpath的对象指针,失败返回NULL
  23. xmlXPathObjectPtr get_nodeset(const xmlChar *xpath);
  24. // 禁止拷贝构造函数和"="操作
  25. CXMLConfig(const CXMLConfig&);
  26. CXMLConfig& operator=(const CXMLConfig&);
  27. private:
  28. string m_strFilename;
  29. xmlDocPtr m_doc;
  30. };

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

  1. int main(int argc, char* argv[])
  2. {
  3. CXMLConfig xmlConfig("..""Debug""XPathConfig.xml");
  4. string strIP = xmlConfig.getXmlString("/main/IP");
  5. int iPort = 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<<"Save Config file success!"<<endl;
  14. }
  15. return 0;
  16. }

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

6.       CXMLConfig类使用小结

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

第一,正确安装了libxml2和iconv库,包括头文件、lib文件和dll文件。注意头文件主要是libxml2和iconv的头文件,lib文件就是两个libxml2.lib和iconv.lib,而dll有三个,即libxml2.dll、iconv.dll和zlib1.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文件最好不大于100M。

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

7.       文末的话

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

转载于:https://www.cnblogs.com/lanzhi/archive/2008/11/05/6471188.html

C++中的XML配置文件编程经验相关推荐

  1. Winform中自定义xml配置文件后对节点进行读取与写入

    场景 Winform中自定义xml配置文件,并配置获取文件路径: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/100522648 ...

  2. Android中Manifest.xml配置文件

    Android中Manifest.xml配置文件里面有很多的属性,下面对部分进行了整理,还没有整理完全. A.基本框架: <?xml version="1.0" encodi ...

  3. IDEA中pom.xml配置文件依赖文件版本号报红的最有效解决办法

    关于IDEA中pom.xml配置文件依赖文件版本号报红的最有效解决办法,简单粗暴直接! 问题产生及解决过程 产生的报错 解决过程 问题产生及解决过程 产生的报错 最近在研究IDEA的使用以及boots ...

  4. Winform中自定义xml配置文件,并配置获取文件路径

    场景 在Winform程序中,需要将一些配置项存到配置文件中,这时就需要自定义xml的配置文件格式.并在一些工具类中去获取配置文件的路径并加载其内容. 关注公众号 霸道的程序猿 获取编程相关电子书.教 ...

  5. C#中读写Xml配置文件常用方法工具类

    场景 有时需要使用配置文件保存一些配置的属性,使其在下次打开时设置仍然生效. 这里以对xml配置文件的读写为例. 1.读取XML配置文. 2.写入XML配置文件. 3.匹配 XPath 表达式的第一个 ...

  6. Mybatis中SqlMapConfig.xml配置文件的使用

    文章目录 SqlMapConfig.xml中配置的内容和顺序如下 properties(属性) SqlMapConfig.xml引用如下 typeAliases(类型别名) mybatis支持别名: ...

  7. SSM中log4j2.xml配置文件的各项标签

    一.背景 本文就专门来讲解下log4j2.xml配置文件的各项标签的意义. 二.配置全解 1.关于配置文件的名称以及在项目中的存放位置 log4j 2.x版本不再支持像1.x中的.properties ...

  8. mybatis中使用XML配置文件方式实现CRUD模板流程

    场景 Eclipse+Mybatis3.4+log4j1.2+mysql5.7 使用新手入门学习Mybatis时实例模板. 实现 Eclipse中新建项目 1.打开EClipse,File--new- ...

  9. hibernate中*.hbm.xml配置文件的各种映射关系配置方法(多对一,多对多)

    多对一: 多个用户对应一个部门,是多对一的关系,要在多方配,即在用户的hbm.xml文件中配 <many-to-one name="department" class=&qu ...

最新文章

  1. 迅为四核4418开发板MiniLinux-CAN总线测试使用文档
  2. 若依前后端分离版手把手教你本地搭建环境并运行项目
  3. 洛谷——P1226 取余运算||快速幂
  4. template里面要做数据渲染,但是数据还没有出来
  5. 反欺诈的这几个重点内容值得您关注
  6. windows 替换字体
  7. 计算机及应用学习顺序,自考计算机及应用专业经验谈
  8. (转)Uncaught TypeError: Cannot set property 'innerHTML' of null
  9. 班级html网页设计实例,静态网页班级网站设计.doc
  10. 关于二进制与十进制互转的方法(简单好学!)
  11. mint-ui引用iconfont图标
  12. HILDON手册(转载自CSDN)
  13. iOS逆向开发(2):获取APP的类声明 | class-dump | dumpdecrypted
  14. 计算机刷新的作用,为什么要刷新 刷新有什么用?
  15. 安卓 每日一题 2020年5-6月问题及答案
  16. sqlserver批量(部分)导出一张表中的数据
  17. CV往哪卷?李飞飞指出三颗「北极星」:具身智能,视觉推理和场景理解
  18. 拼多多砍价显示服务器吃撑了,拼多多砍价网页打不开是为什么
  19. 复杂、高成本的传统IT架构,何以解忧?唯有Nutanix Calm
  20. 送20本纸质书籍,包邮

热门文章

  1. 基于highcharts+easui+java生成时趋图
  2. JVM性能调优监控工具专题一:JVM自带性能调优工具(jps,jstack,jmap,jhat,jstat,hprof)...
  3. 项目讨论:本地手机经销商怎样用移动站点开展本地营销?
  4. 自己选择的路,不后悔
  5. VSphere虚拟化部署(五)
  6. 【算法基础】动态规划解题实例之野营问题
  7. 低字节+高字节+字地址+大端序+小端序全辨析
  8. 【0702作业】输出九九乘法表
  9. spring boot初步
  10. Django学习笔记5-url