最近在学习 Qt 读写XML文件时,思考了一个问题,除了DOM和SAX外,还有没有更快捷的读写XML的方法?经过一番面向搜索引擎编程,我还真发现了一种方法——流读写XML(Write and read by stream)。今天就借此文向大家分享一下这种方法。
三种读写XML方式的对比
| DOM方式 | SAX方式 | 流读写方式 |
|---|---|---|
| XML被存入内存 | 速度快,逐行扫描 | 速度快,一次性传入 |
| 消耗内存较多 | 操作复杂,很难修改 | 不支持修改和删除 |
| 频繁操作较为方便 | 可以随时停止解析,对大型文档友好 | 读写API分离 |
看完对比,你是不是跃跃欲试了?下面我贴上我的练习代码,给你作为参考。
*.pro
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 += targetwidget.h
#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_Hmain.cpp
#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
/* 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
<?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>在学习这种读写方式时,我也踩了一些坑。
- XML有且只有一个根节点。
- 良好的方法封装有利于降低代码(模块)间的耦合;
- 错误信息输出、注释在新学习时必不可少;
好了,本期的分享就到此为止了。如果对你有启发,欢迎转载~