抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

最近在学习 Qt 读写XML文件时,思考了一个问题,除了DOM和SAX外,还有没有更快捷的读写XML的方法?经过一番面向搜索引擎编程,我还真发现了一种方法——流读写XML(Write and read by stream)。今天就借此文向大家分享一下这种方法。

三种读写XML方式的对比

DOM方式 SAX方式 流读写方式
XML被存入内存 速度快,逐行扫描 速度快,一次性传入
消耗内存较多 操作复杂,很难修改 不支持修改和删除
频繁操作较为方便 可以随时停止解析,对大型文档友好 读写API分离

看完对比,你是不是跃跃欲试了?下面我贴上我的练习代码,给你作为参考。

*.pro

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
QT       += core gui xml

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11

DEFINES += QT_DEPRECATED_WARNINGS

SOURCES += \
main.cpp \
widget.cpp

HEADERS += \
widget.h

FORMS += \
widget.ui

qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

widget.h

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
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui
{
class Widget;
}
QT_END_NAMESPACE

class Widget : public QWidget
{
Q_OBJECT

public:
Widget(QWidget *parent = nullptr);
~Widget();
/* Func : write_xml()
* Feat : 将数据写入XML文件并将写入结果在控制台打印
* Auth : AmosWu
* Date : 2020/08/30
* vers : 1.0
*/
int write_xml();

/* Func : read_xml()
* Feat : 将数据从XML文件读取出来并在控制台打印
* Auth : AmosWu
* Date : 2020/08/30
* vers : 1.0
*/
int read_xml();

private:
Ui::Widget *ui;
};
#endif // WIDGET_H

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include "widget.h"
#include <QApplication>
#include <QFile>
#include <QXmlStreamWriter>
#include <QXmlReader>
#include <QtDebug>
#include <QStringList>

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget();
return a.exec();
}

widget.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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
/* Name : widget.cpp
* Date : 2020/08/30
* Auth : Amos
*/

#include "widget.h"
#include "ui_widget.h"
#include <QFile>
#include <QXmlStreamWriter>
#include <QtDebug>
#include <QStringList>

Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
int resWrite = write_xml();
if (resWrite < 0)
{
qDebug() << "ERRO : Write failed!";
}
else
{
qDebug() << "INFO : Write succeed!";
}

int resRead = read_xml();
if(resRead < 0)
{
qDebug() << "ERRO : Read failed!";
}
else
{
qDebug() << "INFO : Read succeed!";
}
}

Widget::~Widget()
{
delete ui;
}

int Widget::write_xml()
{
QStringList urls_list;
QStringList name_list;
QStringList stockList;
urls_list << "https://cn.bing.com"
<< "https://google.com"
<< "https://baidu.com"
<< "https://sougou.com"
<< "https://mi.com";
name_list << "Bing"
<< "Google"
<< "Baidu"
<< "Sougou"
<< "Xiaomi";
stockList << "MSFT.US"
<< "GOOG.US"
<< "BIDU.US"
<< "SOGO.US"
<< "01810.HK";
// url_list.append("https://cn.bing.com");

QFile file("test.xml");
if (!file.open(QFile::WriteOnly | QFile::Text))
{
qDebug() << QString("ERRO : Connot open file test.xml(%2)")
.arg(file.errorString());
return -1;
}
QXmlStreamWriter stream(&file);
stream.setCodec("UTF-8"); // XML编码
stream.setAutoFormatting(true); // 自动格式化
stream.writeStartDocument("1.0", true); // 开始文档(XML声明)
stream.writeStartElement("compangy"); // 创建根节点
for(int i = 0; i < urls_list.size(); i++)
{
stream.writeStartElement("tech");
stream.writeAttribute("name", name_list.at(i));
stream.writeAttribute("url", urls_list.at(i));
stream.writeTextElement("stock_code", stockList.at(i));
stream.writeEndElement();
qDebug() << QString("[ %1-%2 ] write succeed!")
.arg(name_list.at(i))
.arg(urls_list.at(i));
}
stream.writeEndElement();
stream.writeEndDocument();
file.close();
return 1;
}

int Widget::read_xml()
{
QString strFile("test.xml");
QFile file(strFile);
if(!file.open(QFile::ReadOnly | QFile::Text))
{
qDebug() << QString("ERRO : Connot open file %1(%2)")
.arg(strFile)
.arg(file.errorString());
return -1;
}

// 如果读取到文档结尾,并且没有出现错误
QXmlStreamReader reader(&file);

reader.readNext(); // 跳过根节点

while(!reader.atEnd())
{
// 读取下一个记号,它返回记号的类型
QXmlStreamReader::TokenType nType = reader.readNext();

// 下面根据记号的类型来进行不同的输出
// 读取文档的编码方式和版本号
if(nType == QXmlStreamReader::StartDocument)
{
qDebug() << "DocEncoding = "
<< reader.documentEncoding()
<< "DocVersion = "
<< reader.documentVersion();
}

if(reader.isStartElement())
{
if(reader.name() == "tech")
{
qDebug() << "<" << reader.name() << ">";

if (reader.attributes().hasAttribute("name"))

qDebug() << reader.attributes()
.value("name");

if (reader.attributes().hasAttribute("url"))

qDebug() << reader.attributes()
.value("url");

reader.readNext();
}
else if(reader.name() == "stock_code")
{
qDebug() << "<" << reader.name() << ">";
qDebug() << reader.readElementText();
reader.readNext();
}
else
{
reader.readNext();
}

if (nType == QXmlStreamReader::EndElement)
qDebug() << "</" << reader.name() << ">";

// 打印字符
if(nType == QXmlStreamReader::Characters && !reader.isWhitespace())
qDebug() << reader.text();
}
}
// 如果读取过程中出现错误,那么输出错误信息
if(reader.hasError())
{
qDebug() << "ERRO : " << reader.errorString();
return -1;
}

file.close();
return 1;
}

test.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<compangy>
<tech name="Bing" url="https://cn.bing.com">
<stock_code>MSFT.US</stock_code>
</tech>
<tech name="Google" url="https://google.com">
<stock_code>GOOG.US</stock_code>
</tech>
<tech name="Baidu" url="https://baidu.com">
<stock_code>BIDU.US</stock_code>
</tech>
<tech name="Sougou" url="https://sougou.com">
<stock_code>SOGO.US</stock_code>
</tech>
<tech name="Xiaomi" url="https://mi.com">
<stock_code>01810.HK</stock_code>
</tech>
</compangy>

在学习这种读写方式时,我也踩了一些坑。

  1. XML有且只有一个根节点。
  2. 良好的方法封装有利于降低代码(模块)间的耦合;
  3. 错误信息输出、注释在新学习时必不可少;

好了,本期的分享就到此为止了。如果对你有启发,欢迎转载~

评论