expat就是用来解析XML格式的文件的库

XML格式如下

<name><red>apple</red><blue></blue><green>tree</green><pink>hello kitty</pink>
</name>

第一个<name>和最后一个</name>被称为一对键值对,相当于是一个标记。没有"\"的表示是起始标记,含有"\"的是结束标记。中间的所有数据就是<name>这一对键值对包含的内容。

同理<red>和</red>也是一对键值对,也是一个标记。<red>表示起始标记,</red>表示结束标记。"apple"则为<red>这一对键值对的内容。

所以,键值对中可以包含别的键值对。

在了解了XML格式的数据后,我们就可以来解析它了。

直接先给大家看一下主函数

int main()
{XML_Parser parser = XML_ParserCreate(NULL);         // 第一步XML_SetUserData(parser, elempt);                    // 第二步XML_SetElementHandler(parser,StartElementHandler,EndElementHandler);    // 第三步XML_SetCharacterDataHandler(parser,CharacterDataHandler);               // 第四步XML_Parse(parser, buf.c_str(), buf.length(), 1);    //第五步XML_ParserFree(parser);                            //第六步return 0;
}

主函数非常简单,只要前5步就可以解析xml格式的数据,从中得到我们想要的数据,最后释放句柄。

1.第一步。通过  XML_ParserCreate(NULL) ,函数建立一个XML_Parser格式的对象"parser",参数一般为NULL。后面这段话是自己编的(这个对象的概念本人不是非常理解。只能讲讲自己的理解。相当于建立"parser"这一个变量,而这个变量不是我们普通的变量,是XML_Parser类型的。就比如说,int和float类型,虽然占用的字节数一样,但是解析方式完全不一样,他们有各自的解析方式。而XML_Parser类型的也有自己的解析方式,我们必须以XML_Parser能识别的方式来存储数据。XML_ParserCreate函数就是能建立一个XML_Parser类型的数据。这个XML_Parser类型的数据格式是整个解析XML格式的基础。)

2.第二步。XML_SetUserData(parser, char *elempt),parser就是第一步中建立的对象。elempt是一个char *类型的字符串。这个字符串会在expat内部传递给其他函数。这个参数的作用会在下面的实际例子中展示。

3.第三步。XML_SetElementHandler(parser,StartElementHandler,EndElementHandler),parser就是第一步中建立的对象。StartElementHandler是一个回调函数。回调函数的具体概念不讲,可以自行百度。这个函数的作用就是在解析一个XML格式的文件时,每当遇到一个起始标记<name>、<red>、<green>时都会执行一次这个函数,有几个起始标签就会执行几次这个函数。同理。EndElementHandler也是一个回调函数,每当遇到结束标签</name>、</red>、</green>时都会执行一次这个函数,有几个结束标签就会执行几次这个函数。

4.第四步。XML_SetCharacterDataHandler(parser,CharacterDataHandler),parser就是第一步中建立的对象。CharacterDataHandler也是一个回调函数。每当遇到一对键值对中存在内容时,就会执行一次函数。结合第三步,我给大家讲一下整个过程。以本篇最开始的XML格式的数据为例。

4.1解析开始,遇到<name>起始标签,执行一次StartElementHandler。

4.2遇到<red>,因为这个<red>是起始标签,执行一次StartElementHandler。因为<name>和<red>中间没有内容,所以不会执行CharacterDataHandler。如果有的话,会先执行CharacterDataHandler,再执行StartElementHandler。

4.3然后遇到"apple",执行一次CharacterDataHandler

4.4遇到</red>,执行EndElementHandler

4.5后面同理,最后</pink>,执行EndElementHandler,</name>,执行EndElementHandler。解析完成

5.XML_Parse(parser, buf.c_str(), buf.length(), done),parser就是第一步中建立的对象,buf就是将XML格式的数据转换成string格式的数据。buf.c_str()得到char *类型的字符串(这个应该是c++的基础,不懂的话,百度一下,应该能明白)。buf.length(),表示buf的长度。最后一个done表示XML是否解析完成,未完成表示0,完成表示1。

6.上面这5步就是解析XML的步骤了。下面来将上面提到的三个回调函数完成一下

6.1 StartElementHandler。userData就是第二步中的char *elempt传过来的数据。name就是标签名,如果遇到<green>那么就会执行StartElementHandler,name就是green。atts就是标签的属性,比如"<red sex='female' time='2018'>apple<\/red>",那么atts[0] = "sex", atts[1] = "female", atts[2] = "time", atts[3] = "2018"

void StartElementHandler(void *userData, const XML_Char *name, const XML_Char **atts)
{if(!strcmp(name,"name")){cout << name << "\tstart_of_name" << endl;}if(!strcmp(name,"red")){cout << name << "\tstart_of_red" << endl;;cout << atts[0] << "\t" << atts[1]<< "\t" << atts[2]<< "\t" << atts[3]<< endl;}if(!strcmp(name,"blue")){cout << name << "\tstart_of_blue" << endl;}if(!strcmp(name,"green")){cout << name << "\tstart_of_green" << endl;}if(!strcmp(name,"pink")){cout << name << "\tstart_of_pink" << endl;}
}

6.2EndElementHandler。userData就是第二步中的的char *elempt。name就是标签的名字。

void EndElementHandler(void *userData, const XML_Char *name)
{if(!strcmp(name,"name")){cout << name << "\tend_of_name" << endl;}if(!strcmp(name,"red")){cout << name << "\tend_of_red" << endl;}if(!strcmp(name,"blue")){cout << name << "\tend_of_blue" << endl;}if(!strcmp(name,"green")){cout << name << "\tend_of_green" << endl;}if(!strcmp(name,"pink")){cout << name << "\tend_of_pink" << endl;}
}

6.3 CharacterDataHandler。userData就是第二步中的char *elempt,这里我们就用到了。s代表了一个起始标签后的所有内容,比如读到<red>会执行StartElementHandler,然后执行CharacterDataHandler,那么s = "apple</red><blue></blue><green>tree</green><pink>hello kitty</pink></name>"。len就是距离下个标签的长度。上面的例子中,len就是apple的长度。通过s和len就可以把red中的内容读出来了。然后和userDate比较,得到我们需要的东西。

void CharacterDataHandler(void *userData, const XML_Char *s, int len)
{string str2 = s;string str1;cout << str2 << endl;for(int i=0;i<len;i++){str1 += str2.at(i);}cout << str1 << endl;if(!strcmp((char *)userData,str1.c_str())){cout << "this is apple" << endl;}
}

7.上面6点完成了expat函数的设置。接下来就可以具体的解析XML了

附上完整的程序

#include <stdio.h>
#include "expat.h"
#include <string>
#include <string.h>
#include <iostream>
#include <vector>
using namespace std;
char * elempt = "apple";                    //userData
string buf =   "<name>"                  //XML格式的数据"<red sex='female' time='2018'>apple<\/red>""<blue><\/blue>""<green>tree<\/green>""<pink>hello kitty<\/pink>""<\/name>";
//如果是windows环境,<\/name>是对的
//如果是unix环境,则为</name>typedef struct{string red;string blue;string green;string pink;
}fruit;fruit table_temp;vector <fruit> mytable;int flag = 0;void StartElementHandler(void *userData, const XML_Char *name, const XML_Char **atts)        //遇到起始标记的回调函数
{if(!strcmp(name,"name")){//cout << name << "\tstart_of_name" << endl;}if(!strcmp(name,"red"))            //当遇到<red>时,将flag置1{//cout << name << "\tstart_of_red" << endl;;//cout << atts[0] << "\t" << atts[1]<< "\t" << atts[2]<< "\t" << atts[3]<< endl;flag = 1;}if(!strcmp(name,"blue"))            //当遇到<red>时,将flag置2{//cout << name << "\tstart_of_blue" << endl;flag = 2;}if(!strcmp(name,"green"))            //当遇到<red>时,将flag置3{//cout << name << "\tstart_of_green" << endl;flag = 3;}if(!strcmp(name,"pink"))            //当遇到<red>时,将flag置4{//cout << name << "\tstart_of_pink" << endl;flag = 4;}
}void EndElementHandler(void *userData, const XML_Char *name)
{if(!strcmp(name,"pink"))                        //当遇到pink结束标签时,表示4个数据都得到了,将结构体放入vecotr中{//cout << name << "\tend_of_name" << endl;mytable.push_back(table_temp);}if(!strcmp(name,"red")){//cout << name << "\tend_of_red" << endl;}if(!strcmp(name,"blue")){//cout << name << "\tend_of_blue" << endl;}if(!strcmp(name,"green")){//cout << name << "\tend_of_green" << endl;}if(!strcmp(name,"pink")){//cout << name << "\tend_of_pink" << endl;}
}void CharacterDataHandler(void *userData, const XML_Char *s, int len)
{string str2 = s;string str1;//cout << str2 << endl;for(int i=0;i<len;i++)            //str1就是从s中读取到了len长度的值{str1 += str2.at(i);}//cout << str1 << endl;if(!strcmp((char *)userData,str1.c_str())){//cout << "this is apple" << endl;}if(flag == 1)                    //根据flag的值,将str1放入结构体中相应的位置{table_temp.red = str1;}if(flag == 2){table_temp.blue = str1;}if(flag == 3){table_temp.green = str1;}if(flag == 4){table_temp.pink = str1;}
}int main()
{XML_Parser parser = XML_ParserCreate(NULL);     //设置XML解析的对象XML_SetUserData(parser, elempt);                //设置传递给回调函数的参数XML_SetElementHandler(parser,StartElementHandler,EndElementHandler);    //设置遇到起始标记的回调函数和遇到结束标记的回调函数XML_SetCharacterDataHandler(parser,CharacterDataHandler);    //设置遇到正文内容时的回调函数XML_Parse(parser, buf.c_str(), buf.length(), 1);    //正式解析XMLcout << mytable[0].red << endl;cout << mytable[0].blue << endl;cout << mytable[0].green << endl;cout << mytable[0].pink << endl;XML_ParserFree(parser);return 0;
}

8.已经把所有打印都注释了。大家可以打开注释看看结果。其中还有许多细节方面的东西。将字符串换成下面两种情况试一下。

string buf =    "<name>""<red sex='female' time='2018'>apple<\/red>""<blue><\/blue>""<green>tree<\/green>""<pink>hello kitty<\/pink>""<red sex='female' time='2018'>apple<\/red>""<blue><\/blue>""<green>tree<\/green>""<pink>hello kitty<\/pink>""<\/name>";
string buf =    "<name>""<red sex='female' time='2018'>apple<\/red>""<blue><\/blue>""<green>tree<\/green>""<pink>hello kitty<\/pink>""<\/name>""<red sex='female' time='2018'>apple<\/red>""<blue><\/blue>""<green>tree<\/green>""<pink>hello kitty<\/pink>";

当遇到的结束标签为第一个起始标签时,name整个解析过程也就结束了。不管后面有没有内容,都会结束。换句话说,只有解析到和第一个起始标签相同的结束标签(或者为空)时,解析才会停止,不管中间有多少内容,都不会结束。

键值对不需要一对一对的出现,可以只出现起始标记,也可以只出现结束标记。但是这在解析时是可以的。但是在XML中是非法的。所以必须有起始标签和结束标签。

---------------------------------------------------------------更新-------------------------------------------------------------------------------------------------------------

最近看了一下官方的文档,发现程序还待改进。官网中提供了两个解析的例子

主要是在XML_Parse()时有区别,提供了容错机制。

直接拷贝一下官网的XML_Parse

  do {size_t len = fread(buf, 1, sizeof(buf), stdin);done = len < sizeof(buf);if (XML_Parse(parser, buf, (int)len, done) == XML_STATUS_ERROR) {fprintf(stderr,"%" XML_FMT_STR " at line %" XML_FMT_INT_MOD "u\n",XML_ErrorString(XML_GetErrorCode(parser)),XML_GetCurrentLineNumber(parser));return 1;}} while (!done);

主要是用了XML_ErrorString(XML_GetErrorCode(parser)),XML_GetCurrentLineNumber(parser)这两个函数。

用expat解析xml文件c++相关推荐

  1. php xml expat,php 使用expat方式解析xml文件操作示例

    本文实例讲述了php 使用expat方式解析xml文件操作.分享给大家供大家参考,具体如下: test.xml: George John Reminder George2 John2 Reminder ...

  2. python 解析xml 文件: SAX方式

    环境 python:3.4.4 准备xml文件 首先新建一个xml文件,countries.xml.内容是在python官网上看到的. <?xml version="1.0" ...

  3. python中利用lxml模块解析xml文件报错XMLSyntaxError: Opening and ending tag mismatch

    今天在代码中第一次使用lxml解析xml文件时出错了, XMLSyntaxError: Opening and ending tag mismatch: keyEffectiveDate line 2 ...

  4. java xml中的冒号_Java jdom解析xml文件带冒号的属性

    Java jdom解析xml文件带冒号的属性 如果xml文件解析带了冒号的属性,一般都是要特别处理,这里是命名空间,N年前遇到过一次忘记记录,后来也忘了,这次再记录下. 解决了,记录下,分享给大家,百 ...

  5. XML专题:使用NSXMLParser解析xml文件

    使用NSXMLParser解析xml文件 1. 设置委托对象,开始解析      NSXMLParser *parser = [[NSXMLParser alloc] initWithData:dat ...

  6. SAX解析XML文件

    就目前来说,有三种方式可以解析XML文件:DOM.SAX.StAX.DOM将整个XML文件加载到内存中,并构建出节点树:应用程序可以通过遍历节点树的方式来解析XML文件中的各个节点.属性等信息:这种方 ...

  7. Java--Dom解析XML文件

          之前写过几篇关于Java中解析XML文件的方法,不过,感觉不够简单,今天重写了一遍代码,用到的是方法是Dom,其中加入了日志记录功能--Log4j.       好了,不多说了,先把XMl ...

  8. 10. 解析XML文件(SAX/DOM/ElementTre)

    XML的全称是eXtensible Markup Language, 意为可扩展的标记语言, 是一种用于标记电子文件使其具有结构性的标记语言.以XML结构存储数据的文件就是XML文件,它被设计用来传输 ...

  9. Dom方法,解析XML文件

    Dom方法,解析XML文件的基本操作 1 package com.demo.xml.jaxp; 2 3 import java.io.IOException; 4 5 import javax.xml ...

最新文章

  1. Objective-C学习笔记_命令行参数获取
  2. 漫谈C++重载运算符
  3. babymips(下) 寒假逆向生涯(14/100)
  4. 02_tensorflow2环境安装、CUDA 10.1 和CUDnn 7.6.5 版本安装、https://tensorflow.google.cn/overview中概述翻译
  5. PHP保留小数三种方法
  6. 上半年产品原型设计Axure rp稿(持续更新)
  7. dvd刻录软件_如何在Windows 7中刻录照片和视频DVD(无需额外的软件)
  8. 【WEB API项目实战干货系列】- API访问客户端(WebApiClient适用于MVC/WebForms/WinForm)(四)
  9. 爬虫——多线程糗事百科案例
  10. flask创建mysql表_Flask-SQLAlchemy 无法创建数据库???
  11. 新编译的GDAL1.9 C/C++ C# Python版本
  12. html基础入门----简单动画
  13. win7企业版怎么都激活不了,看这里
  14. matlab小波变换,图像处理
  15. 说明文中国第一台亮子计算机揭秘,2018届九年级语文中考复习(河南)课件:第2部分 第二讲 说明文阅读 2017名题强化训练.ppt...
  16. Python3批量修改图片像素
  17. 弘辽科技:如何写出自带流量的标题
  18. 解决Win10锁屏超1分钟,显示器关闭问题
  19. 监控相机IQ调试策略
  20. JAVA项目的目录结构分析

热门文章

  1. 5G来临,互联网如何再次重构电影产业?
  2. roast和roasting区别_西餐干加热之有些小复杂的Roast
  3. 中国农民贫困的真正原因(转)
  4. BrandExample.Criteria方法 实现搜索框输入关键字模糊查询
  5. 移动互联网,是破坏性创新吗?
  6. 【2021Java最新学习路线】java审批工作流实现
  7. Cell R-CNN V3: A Novel Panoptic Paradigm for Instance Segmentation in Biomedical Images
  8. 同频干扰信号 仿真 matlab,直接序列扩频系统抗同频干扰的MATLAB仿真8.29(可靠性分析12月)...
  9. 同频信号干扰解决办法
  10. 工厂装配线 3D 可视化看板,让管理者快速定位生产瓶颈!