Qt 流读写XML

Qt 流读写XML

新知笔记 📅 2020-08-31 ⏱ 约 19 分钟
✦
AI 摘要AI Abstract
  • Qt流读写XML采用QXmlStreamWriter和QXmlStreamReader,实现高效的一次性读写,适合快速…
  • 相比DOM和SAX,流读写速度快、内存占用低,但不支持文档修改和删除操作。
  • 流读写API分离,写入使用QXmlStreamWriter,读取使用QXmlStreamReader,代码结构清晰。
  • 示例代码展示了如何将数据写入XML文件并读取,包含错误处理和UTF-8编码设置。

最近在学习 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 += target

widget.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_H

main.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>

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

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

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

评论