一杯清酒邀明月
天下本无事,庸人扰之而烦耳。

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

XML格式如下

1 <name>
2     <red>apple</red>
3     <blue></blue>
4     <green>tree</green>
5     <pink>hello kitty</pink>
6 </name>

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

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

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

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

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

 1 int main()
 2 {
 3     XML_Parser parser = XML_ParserCreate(NULL);         // 第一步
 4     XML_SetUserData(parser, elempt);                    // 第二步
 5     XML_SetElementHandler(parser,StartElementHandler,EndElementHandler);    // 第三步
 6     XML_SetCharacterDataHandler(parser,CharacterDataHandler);               // 第四步
 7  
 8     XML_Parse(parser, buf.c_str(), buf.length(), 1);    //第五步
 9  
10     XML_ParserFree(parser);                            //第六步
11     return 0;
12 }

主函数非常简单,只要前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"

 1 void StartElementHandler(void *userData, const XML_Char *name, const XML_Char **atts)
 2 {
 3     if(!strcmp(name,"name"))
 4     {
 5         cout << name << "\tstart_of_name" << endl;
 6     }
 7     if(!strcmp(name,"red"))
 8     {
 9         cout << name << "\tstart_of_red" << endl;;
10         cout << atts[0] << "\t" << atts[1]<< "\t" << atts[2]<< "\t" << atts[3]<< endl;
11     }
12     if(!strcmp(name,"blue"))
13     {
14         cout << name << "\tstart_of_blue" << endl;
15     }
16     if(!strcmp(name,"green"))
17     {
18         cout << name << "\tstart_of_green" << endl;
19     }
20     if(!strcmp(name,"pink"))
21     {
22         cout << name << "\tstart_of_pink" << endl;
23     }
24 }

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

 1 void EndElementHandler(void *userData, const XML_Char *name)
 2 {
 3     if(!strcmp(name,"name"))
 4     {
 5         cout << name << "\tend_of_name" << endl;
 6     }
 7     if(!strcmp(name,"red"))
 8     {
 9         cout << name << "\tend_of_red" << endl;
10     }
11     if(!strcmp(name,"blue"))
12     {
13         cout << name << "\tend_of_blue" << endl;
14     }
15     if(!strcmp(name,"green"))
16     {
17         cout << name << "\tend_of_green" << endl;
18     }
19     if(!strcmp(name,"pink"))
20     {
21         cout << name << "\tend_of_pink" << endl;
22     }
23 }

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比较,得到我们需要的东西。

 1 void CharacterDataHandler(void *userData, const XML_Char *s, int len)
 2 {
 3     
 4     string str2 = s;
 5     string str1;
 6     cout << str2 << endl;
 7     for(int i=0;i<len;i++)
 8     {
 9         str1 += str2.at(i);
10     }
11     cout << str1 << endl;
12     if(!strcmp((char *)userData,str1.c_str()))
13     {
14         cout << "this is apple" << endl;
15     }
16 }

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

附上完整的程序

  1 #include <stdio.h>
  2 #include "expat.h"
  3 #include <string>
  4 #include <string.h>
  5 #include <iostream>
  6 #include <vector>
  7 using namespace std;
  8 char * elempt = "apple";                    //userData
  9 string buf =    "<name>"                  //XML格式的数据
 10             "<red sex='female' time='2018'>apple<\/red>"
 11             "<blue><\/blue>"
 12             "<green>tree<\/green>"
 13             "<pink>hello kitty<\/pink>"
 14             "<\/name>";
 15 //如果是windows环境,<\/name>是对的
 16 //如果是unix环境,则为</name>
 17  
 18 typedef struct{
 19     string red;
 20     string blue;
 21     string green;
 22     string pink;
 23 }fruit;
 24  
 25 fruit table_temp;
 26  
 27 vector <fruit> mytable;
 28  
 29 int flag = 0;
 30  
 31 void StartElementHandler(void *userData, const XML_Char *name, const XML_Char **atts)        //遇到起始标记的回调函数
 32 {
 33     if(!strcmp(name,"name"))
 34     {
 35         //cout << name << "\tstart_of_name" << endl;
 36     }
 37     if(!strcmp(name,"red"))            //当遇到<red>时,将flag置1
 38     {
 39         //cout << name << "\tstart_of_red" << endl;;
 40         //cout << atts[0] << "\t" << atts[1]<< "\t" << atts[2]<< "\t" << atts[3]<< endl;
 41         flag = 1;
 42     }
 43     if(!strcmp(name,"blue"))            //当遇到<red>时,将flag置2
 44     {
 45         //cout << name << "\tstart_of_blue" << endl;
 46         flag = 2;
 47     }
 48     if(!strcmp(name,"green"))            //当遇到<red>时,将flag置3
 49     {
 50         //cout << name << "\tstart_of_green" << endl;
 51         flag = 3;
 52     }
 53     if(!strcmp(name,"pink"))            //当遇到<red>时,将flag置4
 54     {
 55         //cout << name << "\tstart_of_pink" << endl;
 56         flag = 4;
 57     }
 58 }
 59  
 60 void EndElementHandler(void *userData, const XML_Char *name)
 61 {
 62     if(!strcmp(name,"pink"))                        //当遇到pink结束标签时,表示4个数据都得到了,将结构体放入vecotr中
 63     {
 64         //cout << name << "\tend_of_name" << endl;
 65         mytable.push_back(table_temp);
 66     }
 67     if(!strcmp(name,"red"))
 68     {
 69         //cout << name << "\tend_of_red" << endl;
 70     }
 71     if(!strcmp(name,"blue"))
 72     {
 73         //cout << name << "\tend_of_blue" << endl;
 74     }
 75     if(!strcmp(name,"green"))
 76     {
 77         //cout << name << "\tend_of_green" << endl;
 78     }
 79     if(!strcmp(name,"pink"))
 80     {
 81         //cout << name << "\tend_of_pink" << endl;
 82     }
 83 }
 84  
 85 void CharacterDataHandler(void *userData, const XML_Char *s, int len)
 86 {
 87     string str2 = s;
 88     string str1;
 89     //cout << str2 << endl;
 90     for(int i=0;i<len;i++)            //str1就是从s中读取到了len长度的值
 91     {
 92         str1 += str2.at(i);
 93     }
 94     //cout << str1 << endl;
 95     if(!strcmp((char *)userData,str1.c_str()))
 96     {
 97         //cout << "this is apple" << endl;
 98     }
 99     if(flag == 1)                    //根据flag的值,将str1放入结构体中相应的位置
100     {
101         table_temp.red = str1;
102     }
103     if(flag == 2)
104     {
105         table_temp.blue = str1;
106     }
107     if(flag == 3)
108     {
109         table_temp.green = str1;
110     }
111     if(flag == 4)
112     {
113         table_temp.pink = str1;
114     }
115 }
116  
117 int main()
118 {
119     XML_Parser parser = XML_ParserCreate(NULL);     //设置XML解析的对象
120     XML_SetUserData(parser, elempt);                //设置传递给回调函数的参数
121     XML_SetElementHandler(parser,StartElementHandler,EndElementHandler);    //设置遇到起始标记的回调函数和遇到结束标记的回调函数
122     XML_SetCharacterDataHandler(parser,CharacterDataHandler);    //设置遇到正文内容时的回调函数
123  
124     XML_Parse(parser, buf.c_str(), buf.length(), 1);    //正式解析XML
125     
126  
127     cout << mytable[0].red << endl;
128     cout << mytable[0].blue << endl;
129     cout << mytable[0].green << endl;
130     cout << mytable[0].pink << endl;
131  
132     XML_ParserFree(parser);
133     return 0;
134 }

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

 1 string buf =    "<name>"
 2                 "<red sex='female' time='2018'>apple<\/red>"
 3                 "<blue><\/blue>"
 4                 "<green>tree<\/green>"
 5                 "<pink>hello kitty<\/pink>"
 6  
 7                 "<red sex='female' time='2018'>apple<\/red>"
 8                 "<blue><\/blue>"
 9                 "<green>tree<\/green>"
10                 "<pink>hello kitty<\/pink>"
11             "<\/name>";
 1 string buf =    "<name>"
 2             "<red sex='female' time='2018'>apple<\/red>"
 3             "<blue><\/blue>"
 4             "<green>tree<\/green>"
 5             "<pink>hello kitty<\/pink>"
 6         "<\/name>"
 7             "<red sex='female' time='2018'>apple<\/red>"
 8             "<blue><\/blue>"
 9             "<green>tree<\/green>"
10                 "<pink>hello kitty<\/pink>";

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

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

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

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

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

直接拷贝一下官网的XML_Parse

 1   do {
 2     size_t len = fread(buf, 1, sizeof(buf), stdin);
 3     done = len < sizeof(buf);
 4     if (XML_Parse(parser, buf, (int)len, done) == XML_STATUS_ERROR) {
 5       fprintf(stderr,
 6               "%" XML_FMT_STR " at line %" XML_FMT_INT_MOD "u\n",
 7               XML_ErrorString(XML_GetErrorCode(parser)),
 8               XML_GetCurrentLineNumber(parser));
 9       return 1;
10     }
11   } while (!done);

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

posted on 2023-06-27 16:00  一杯清酒邀明月  阅读(302)  评论(0编辑  收藏  举报