服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C/C++ - C++读入XML文件示例

C++读入XML文件示例

2021-04-21 15:43新科程序猿 C/C++

本篇文章主要介绍了C++读入XML文件,读取和设置xml配置文件是最常用的操作,TinyXML是一个开源的解析XML的C++解析库,感兴趣的小伙伴们可以参考一下。

最近要做一个VRP的算法,测试集都是放在Xml文件中,而我的算法使用C++来写,所以需要用C++来读取Xml文件。

在百度上搜“C++读取Xml文件”,可以出来很多博客,大多数是关于tinyXml的,所以这篇博文也是讲述如何用tinyXML来读取XML文件。

tinyXml是一个免费开源的C++库,可以到官网上下载:https://sourceforge.net/projects/tinyxml/

下载下来解压之后,可以看到下面这些文件:

C++读入XML文件示例

我是在windows下用VS来写C++的,按照@marchtea的说法,只需要直接打开tinyxml.sln就可以,不过我还是用了笨办法:

  • 把tinystr.cpp, tinyxml.cpp, tinyxmlerror.cpp, tinyxmlparser.cpp, tinystr.h, tinyxml.h拷贝到工程目录下;
  • 然后加入头文件引用:#include "tinystr.h"   #include "tinyxml.h"。

接下来就来分享一下我读取VRP问题中的solomon benchmark的方法,这些方法都是参考自tinyXml的官方教程,在下载的文件夹中有"doc"子文件夹,打开它,有一个叫做"tutorial0"的html文件,打开它可以看到详细的教程。

OK,now begins!

我要读取的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
<!-- 要读取的Xml文件 -->
 
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 
<instance>
 
  <network>
 
    <nodes>
 
      <node id="0" type="0">
 
        <cx>40.0</cx>
 
        <cy>50.0</cy>
 
      </node>
 
    <!-- 有N+1个这样的node节点 -->
 
    </nodes
 
  </network>
 
  <requests>
 
    <request id="1" node="1">
 
      <tw>
 
        <start>145</start>
 
        <end>175</end>
 
      </tw>
 
      <quantity>20.0</quantity>
 
      <service_time>10.0</service_time>
 
    </request>
 
    <!-- 有N个这样的request节点 -->
 
  </requests>
 
</instance>

这里稍微解释一下为什么nodes节点的数目会比requests节点多1个。这是因为nodes节点包括了顾客节点(N个)和仓库节点(1个),而requests属性只属于顾客节点。

我是把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
// Customer.h
 
#ifndef _Customer_H
 
#define _Customer_H
 
 
 
class Customer{
 
public:
 
  Customer(int id=0, float x=0, float y=0, float startTime=0, float endTime=0, float quantity=0, float serviceTime=0);
 
  void setId(int id);  // 设置成员id的值
 
  void setX(float x);  // 设置成员x的值
 
  void setY(float y);  // 设置成员y的值
 
  void setStartTime(float startTime); // 设置成员startTime的值
 
  void setEndTime(float endTime);   // 设置成员endTime的值
 
  void setQuantity(float quantity);  // 设置成员quantity的值
 
  void setServiceTime(float serviceTime); // 设置成员serviceTime的值
 
  void show(); // 显示顾客节点信息
 
private:
 
  int id;
 
  float x;
 
  float y;
 
  float startTime;
 
  float endTime;
 
  float quantity;
 
  float serviceTime;
 
};
 
#endif

OK,那么现在开始贴一下main.cpp代码(Customer.cpp比较简单,就不贴了)

?
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
// main.cpp
 
#include "Customer.h"
 
#include "tinystr.h"
 
#include "tinyxml.h"
 
#include<iostream>
 
#include<vector>
 
#include<string>
 
#include<stdlib.h>
 
#include<iomanip>
 
 
 
using namespace std;
 
static const int NUM_OF_CUSTOMER = 51;    //顾客数量
 
static const char* FILENAME = "RC101_050.xml"; //文件名
 
 
 
int main(){
 
  vector<Customer *> customerSet(0); // 顾客集,每个元素是Customer对象的指针
 
  int i,j,k,count;
 
  int temp1;  // 存放整型数据
 
  float temp2; // 存放浮点型数据
 
  Customer* customer;  // 临时顾客节点指针
 
  for (i=0; i<NUM_OF_CUSTOMER; i++) { // 先初始化顾客集
 
    customer = new Customer();
 
    customerSet.push_back(customer);
 
  }
 
  TiXmlDocument doc(FILENAME);  // 读入XML文件
 
  if(!doc.LoadFile()) return -1; // 如果无法读取文件,则返回
 
  TiXmlHandle hDoc(&doc);     // hDoc是&doc指向的对象
 
  TiXmlElement* pElem;      // 指向元素的指针
 
  pElem = hDoc.FirstChildElement().Element(); //指向根节点
 
  TiXmlHandle hRoot(pElem);    // hRoot是根节点
 
 
 
  // 读取x,y,它们放在network->nodes->node节点中
 
  TiXmlElement* nodeElem = hRoot.FirstChild("network").FirstChild("nodes").FirstChild("node").Element(); //当前指向了node节点
 
  count = 0; // 记录移动到了哪个node节点,并且把该node节点的信息录入到顺序对应的customer中
 
  for(nodeElem; nodeElem; nodeElem = nodeElem->NextSiblingElement()) { // 挨个读取node节点的信息
 
    customer = customerSet[count]; // 当前顾客节点,注意不能赋值给一个新的对象,否则会调用复制构造函数
 
    TiXmlHandle node(nodeElem); // nodeElem所指向的节点
 
    TiXmlElement* xElem = node.FirstChild("cx").Element(); // cx节点
 
    TiXmlElement* yElem = node.FirstChild("cy").Element(); // cy节点
 
    nodeElem->QueryIntAttribute("id", &temp1); //把id放到temp1中,属性值读法
 
    customer->setId(temp1);    
 
    temp2 = atof(xElem->GetText());  // char转float
 
    customer->setX(temp2);
 
    temp2 = atof(yElem->GetText());
 
    customer->setY(temp2);
 
    count++;
 
  }
 
 
 
  // 读取其余信息
 
  TiXmlElement* requestElem = hRoot.FirstChild("requests").FirstChild("request").Element(); // 指向了request节点
 
  count = 1;
 
  for(requestElem; requestElem; requestElem = requestElem->NextSiblingElement()) {
 
    customer = customerSet[count];   // 当前顾客节点,注意不能赋值给一个新的对象,否则会调用复制构造函数
 
    TiXmlHandle request(requestElem); // 指针指向的对象
 
    TiXmlElement* startTimeElem = request.FirstChild("tw").FirstChild("start").Element(); // start time
 
    TiXmlElement* endTimeElem = request.FirstChild("tw").FirstChild("end").Element();   // end time
 
    TiXmlElement* quantityElem = request.FirstChild("quantity").Element();        // quantity
 
    TiXmlElement* serviceTimeElem = request.FirstChild("service_time").Element();     // service time
 
    // 分别读取各项数据
 
    temp2 = atof(startTimeElem->GetText());
 
    customer->setStartTime(temp2);
 
    temp2 = atof(endTimeElem->GetText());
 
    customer->setEndTime(temp2);
 
    temp2 = atof(quantityElem->GetText());
 
    customer->setQuantity(temp2);
 
    temp2 = atof(serviceTimeElem->GetText());
 
    customer->setServiceTime(temp2);
 
    count++;
 
  }
 
 
 
  // 将读取到的信息输出到控制台
 
  cout<<setiosflags(ios_base::left)<<setw(6)<<"id"<<setw(6)<<"x"<<setw(6)<<
 
    "y"<<setw(12)<<"startTime"<<setw(12)<<"endTime"<<setw(12)<<"quantity"<<setw(14)<<"serviceTime"<<endl;
 
  for(i=0; i<NUM_OF_CUSTOMER; i++) {
 
    customer = customerSet[i];
 
    customer->show();
 
  }
 
  system("pause");
 
  return 0;
 
}

在解释main.cpp的内容之前,先解释一下一些数据类型(只是个人理解,欢迎纠错):

  • TiXmlDocument:文件节点,把Xml文件的内容读入到该类型变量中
  • TiXmlElement*:指向节点的指针
  • TiXmlHandle:节点的实例,也就是TiXmlElement所指向的对象
  • FirstChild("nodeName"):第一个名字为“nodeName”的子节点
  • NextSiblingElement():下一个兄弟节点元素,它们有相同的父节点
  • QueryIntAttribute("attributeName", &var):把节点属性名为attributeName的属性值以int类型赋值给var变量
  • GetText():获取当前节点元素的内容,即包含在<node>text</node>中的text

OK,有了以上一些简单的知识积累,就可以很方便地读取Xml文件了,现在截取xml的部分来讲解:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<instance>
 
  <network>
 
    <nodes>
 
      <node id="0" type="0">
 
        <cx>40.0</cx>
 
        <cy>50.0</cy>
 
      </node>
 
    <!-- 有N+1个这样的node节点 -->
 
    </nodes
 
  </network>
 
  .....
 
</instance>

在这部分我们会把顾客的id,坐标x,y都读入到Customer对象中。 

1. 首先我们得到了文件节点hDoc,现在我们要进入根节点"instance":

?
1
2
3
4
5
TiXmlElement* pElem;      // 指向元素的指针
 
pElem = hDoc.FirstChildElement().Element(); //指向根节点
 
TiXmlHandle hRoot(pElem);    // hRoot是根节点

根节点"instance"是文件节点的第一个子节点,所以用 pElem = hDoc.FirstChildElement().Element() 就可以使得指针pElem指向"instance",hRoot是pElem所指向的对象。

2. 现在我们需要进入到“node”节点中,遍历其兄弟节点,将所有数据读入。下面的语句可以将第一个“node”节点的指针赋值给nodeElem:

?
1
TiXmlElement* nodeElem = hRoot.FirstChild("network").FirstChild("nodes").FirstChild("node").Element(); //当前指向了node节点

节点的id值放在"node"节点的属性"id"中:

?
1
nodeElem->QueryIntAttribute("id", &temp1); //把id放到temp1中,属性值读法

然后坐标x, y的值放在“node”节点的子节点"cx"和"cy"的内容(text)中,所以我们这样来读取:

?
1
2
3
TiXmlElement* xElem = node.FirstChild("cx").Element(); // cx节点
 
temp2 = atof(xElem->GetText());  // char转float

函数atof在库<stdlib>中,用以将char数组转化为浮点数。

通过1,2两步,我们已经把第一个“node”节点的id, x, y的值读入到对象中,然后只需要把遍历所有的兄弟节点即可:

?
1
2
3
4
5
for(nodeElem; nodeElem; nodeElem = nodeElem->NextSiblingElement()) {
 
......
 
}

读入requests节点下的startTime, endTime, quantity, serviceTime等值的方法也是一样的,详情参考main.cpp代码。

运行结果如下:

C++读入XML文件示例

总结:

其实说白了读取Xml文件的关键在于:

  • 移动指针到所要读取数据的节点中; 
  • 若是读取属性值,则使用QueryIntAttribute方法直接读取;
  • 若读取的是节点的内容,则使用getText()方法读取;
  • 连续的数据具有兄弟节点关系,使用NextSiblingElement()方法来指向下一个兄弟节点

 后记:

这篇博文只介绍了如何读取Xml文件,至于如何写入Xml文件,请参考tinyXml的官方教程,讲的特别清楚,特别良心。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:http://www.cnblogs.com/yyh1993/p/6130455.html

延伸 · 阅读

精彩推荐