Qt reads xml files

QXmlStreamReader

The QXmlStreamReader class provides us with a fast way to read xml files through a simple streaming API. He is faster than the SAX parsing method used by Qt itself.

The so-called streaming read is to read an xml document into a series of marked streams, similar to SAX. The main difference between the QXmlStreamReader class and SAX is the way these tags are parsed. When using SAX parsing, the application must provide some processors (callback functions) to handle a series of so-called xml events from the parser, and different xml tags will trigger different events for corresponding processing. With QXmlStreamReader, the application itself can drive the entire loop, pulling xml tags one by one from the parser. This action can be done by readNext(), which will read the next complete token, and then its tokenType(). Then, we can use a series of convenient functions, such as isStartElement(), text(), etc. to determine or get the specific read content. The advantage of this pull mode (pull) parsing method is that we can separate the parsing of an xml document into multiple functions, and use a separate function for different tags.

A typical usage of the QXmlStreamReader class is as follows:

    QXmlStreamReader xml;
    ...
    while (!xml.atEnd()) {
          xml.readNext();
          ... // do processing
    }
    if (xml.hasError()) {
          ... // do error handling
    }

If an error occurs during parsing, atEnd() and hasError() will return true, and error() will return the specific error type that occurred. The errorString(), lineNumber(), columnNumber() and characterOffset() functions can be used to obtain specific error information. Generally, we use these functions to construct an error string to prompt the user for specific error information. At the same time, in order to simplify the application code, QXmlStreamReader also provides a raiseError() mechanism that allows us to trigger a custom error message when necessary.

QXmlStreamReader is an incremental parser. It can handle the situation that the document cannot be processed at once, for example, the xml file comes from multiple files or from the network. When QXmlStreamReader parses all the data but the xml document is incomplete, it will return a PrematureEndOfDocumentError type error. Then, when more data arrives, it recovers from this error and proceeds to call readNext() to parse the new data.

QXmlStreamReader is not very memory-intensive, because it does not store the entire xml document tree in memory, only the tags it is currently parsing. In addition, QXmlStreamReader uses QStringRef to parse all string data instead of real QString objects, which can avoid unnecessary small string memory allocation costs. QStringRef is a simple wrapper for QString or its substrings, and provides some APIs similar to the QString class, but it does not allocate memory, and uses reference counting at the bottom to share data. We can call toString() of QStringRef to get a real QString object when needed.

read xml file

  1. example.xml

<?xml version="1.0" encoding="UTF-8"?>
<labels map="demo1" ver="1.0">   
    <label id="1802232"> 
         <x>1568</x> 
         <y>666</y> 
     </label> 
 
    <label id="1802230"> 
         <x>1111</x> 
         <y>622</y> 
     </label> 
</labels>
#ifndef XMLREAGER_H
#define XMLREAGER_H
#include <QXmlStreamReader>
 
class xmlreader
{
public:
    xmlreader();
    bool readFile(const QString &fileName);
 
private:
    void readlabelsElement();   //读取label标签
    void readlabelElement();    //读取label标签
    void readxElement();        //读取x标签
    void readyElement();        //读取y标签
 
    void skipUnknownElement();  //跳过未知标签
    QXmlStreamReader reader;    
};
 
#endif // XMLREAGER_H
#include "xmlreader.h"
#include <iostream>
#include <QDebug>
#include <QFile>
 
xmlreader::xmlreader()
{
 
}
 
bool xmlreader::readFile(const QString &fileName)
{
   //以只读和文本方式打开文件,如果打开失败输出错误日志,并且返回false 
    QFile file(fileName);
    if (!file.open(QFile::ReadOnly | QFile::Text)) {
        std::cerr << "Error: Cannot read file " << qPrintable(fileName)
                  << ": " << qPrintable(file.errorString())
                  << std::endl;
        return false;
    }
 
    //将文件设置成xml阅读器的输入设备
    reader.setDevice(&file);
    reader.readNext();   //直接读取下一个节点,因为首先读到的标签是XML文件的头部(第一行)
 
    while (!reader.atEnd()) //外部循环,未到文件结尾就一直循环读取
    {
        if (reader.isStartElement()) //外部分支,如果不是起始标签,则直接读取下一个节点
        {
 
            if (reader.name() == "labels") //内部分支,如果根节点不是 == labels,
                                           //说明读取的文件是错误的
            {
                qDebug() << reader.name();//通过qDebug()输出当前节点的名字,这里输出labels
                readlabelsElement();      //读取labels节点的内容
            } 
            else 
            {    //raiseError()函数用来自定义输出错误日志的内容,这里输出Not a labels file
                reader.raiseError(QObject::tr("Not a labels file"));
            }
        } 
        else 
        {
            reader.readNext();
        }
    }
    //关闭文件,如果读取发生错误(hasError())或者文件有错误,输出错误信息,返回false,
    file.close();
    if (reader.hasError()) {
        std::cerr << "Error: Failed to parse file "
                  << qPrintable(fileName) << ": "
                  << qPrintable(reader.errorString()) << std::endl;
        return false;
    } else if (file.error() != QFile::NoError) {
        std::cerr << "Error: Cannot read file " << qPrintable(fileName)
                  << ": " << qPrintable(file.errorString())
                  << std::endl;
        return false;
    }
 
    return true;
}
 
void xmlreader::readlabelsElement()
{
    reader.readNext();//读取了根节点labels后,继续读取下一个节点
    while (!reader.atEnd()) 
    {
        if (reader.isEndElement())
        {
            reader.readNext();
            break;      //如果是结束节点,则结束循环
     //循环执行下去,读到的第一个结束节点是</labels>,而不是</label>;
     //这是执行readlabelElement()函数中得到的结果,当读到</label>时,
     //该函数跳出循环并读取下一个节点,而下一个节点是<label>或者</labels>
        }
 
        if (reader.isStartElement()) 
        {
            if (reader.name() == "label") 
            {   //获得label的attributes()值,也就是id,转换成字符串输出
                qDebug() << reader.attributes().value("id").toString();
                qDebug() << reader.name();
                readlabelElement();
            } 
            else 
            {
                skipUnknownElement();//未知节点直接跳过
            }
        }
        else
        {
            reader.readNext();
        }
    }
}
 
void xmlreader::readlabelElement()
{
 
    reader.readNext();
    while (!reader.atEnd()) 
    {
        if (reader.isEndElement()) 
        {
            reader.readNext();
            break;
        }
 
        if (reader.isStartElement()) 
        {
            if (reader.name() == "x") 
            {
                readxElement();
            } 
            else if (reader.name() == "y") 
            {
                readyElement();
            } 
            else 
            {
                skipUnknownElement();
            }
        }  
        else 
        {
            reader.readNext();
        }
    }
}
 
void xmlreader::readxElement()
{
 
    QString x = reader.readElementText();
    qDebug() <<"x:" << x;
    if (reader.isEndElement())
        reader.readNext();
 
}
void xmlreager::readyElement()
{
 
    QString y = reader.readElementText();//执行这个函数以后,y获得了坐标值,并且当前节点
                                         //自动变成结束节点</y>
    qDebug() << "y:" << y;
    if (reader.isEndElement())
        reader.readNext();  //在这里,读取下一个节点,就是</label>
 
}
//是一个递归函数
void xmlreader::skipUnknownElement()
{
    reader.readNext();
    while (!reader.atEnd()) {
        if (reader.isEndElement()) {
            reader.readNext();
            break;
        }
 
        if (reader.isStartElement()) {
            skipUnknownElement();//函数的递归调用
        } else {
            reader.readNext();
        }
    }
}
#include <QtCore/QCoreApplication>
#include "xmlreader.h"
#include <iostream>
 
int main(int argc, char *argv[])
{
 
    QCoreApplication a(argc, argv);
    xmlreader reader;
    reader.readFile("labels.xml");
    return a.exec();
}

The reading result is shown in the figure below:

  1. teachers.xml:

Among them, the teacher information on the third floor (floor3) of the school (school), and the information of a student.

6 teachers, 1 student

<?xml version="1.0" ?>
<school>
   <floor3 id="3" time="2019/10/11">
    <teacher>
        <entry name="Job">
            <age>30</age>
            <sport>soccer</sport>
        </entry>
        <entry name="Tom">
            <age>32</age>
            <sport>swimming</sport>
        </entry>
    </teacher>

    <teacher>
        <entry name="Job2">
            <age>30</age>
            <sport>soccer</sport>
        </entry>
        <entry name="Tom">
            <age>32</age>
            <sport>swimming</sport>
        </entry>
    </teacher>

    <teacher>
        <entry name="Job3">
            <age>30</age>
            <sport>soccer</sport>
        </entry>
        <entry name="Tom">
            <age>32</age>
            <sport>swimming</sport>
        </entry>
    </teacher>
    
    <teacher>
        <entry name="Job4">
            <age>30</age>
            <sport>soccer</sport>
        </entry>
        <entry name="Tom">
            <age>32</age>
            <sport>swimming</sport>
        </entry>
    </teacher>
    
    <teacher>
        <entry name="Job5">
            <age>30</age>
            <sport>soccer</sport>
        </entry>
        <entry name="Tom">
            <age>32</age>
            <sport>swimming</sport>
        </entry>
    </teacher>

    <student>
        <entry name="Lily">
            <age>20</age>
            <sport>dancing</sport>
        </entry>
        <entry name="Keith">
            <age>21</age>
            <sport>running</sport>
        </entry>
    </student>

    <teacher>
        <entry name="Job6">
            <age>30</age>
            <sport>soccer</sport>
        </entry>
        <entry name="Tom">
            <age>32</age>
            <sport>swimming</sport>
        </entry>
    </teacher>
   </floor3>
</school>

Notice:

The focus is on the code in the function, which can be used after transplantation.

Remember to organize the variables "used for counting".

There is also the address of the file, which must be replaced.

The content of the XML file is first read into a variable, and then the content of the variable is analyzed.

It is greatly affected by the encoding format of the XML file. If there is a phenomenon of garbled Chinese characters, use this method with caution, and it may not be able to read.

#include <QtCore/QCoreApplication>
#include <QXmlStreamReader>
#include <QFile>
#include <iostream>

void ReadXml()
{
    //用来计数
    int teacherCount = 0;
    int ageCount = 0;
    int sanlouCount = 0;
    int schoolCount = 0;

    //读取文件
    QString fileName = "D:/JBXML/teachers.xml";
    QFile file(fileName);
    if (!file.open(QFile::ReadOnly | QFile::Text))
    {
        return ;
    }
    //QXmlStreamReader操作任何QIODevice.
    QXmlStreamReader xml(&file);

    //解析XML,直到结束
    while (!xml.atEnd() && !xml.hasError())
    {
        //读取下一个element.
        QXmlStreamReader::TokenType token = xml.readNext();

        /*以下内容用于分析读取的内容,可以将每一个读取到的标签名字打印出来*//*
        if (token == QXmlStreamReader::Invalid)
        {
            //如果有读取错误,将被打印出来
            std::cout << xml.errorString().toStdString();
        }
        std::cout << xml.tokenString().toStdString() << "\t";
        std::cout << xml.name().toString().toStdString() << std::endl;*/
        /*显示这个分析过程,你会看到很清晰的读取过程*/

        //如果获取的仅为StartDocument,则进行下一个
        if (token == QXmlStreamReader::StartDocument)
        {
            continue;
        }

        //如果获取了StartElement,则尝试读取
        if (token == QXmlStreamReader::StartElement)
        {
            //如果为person,则对其进行解析
            if (xml.name() == "teacher")
            {
                teacherCount++;
            }

            if (xml.name() == "age")
            {
                ageCount++;
            }

            if (xml.name() == "floor3")
            {
                sanlouCount++;
            }

            if (xml.name() == "school")
            {
                schoolCount++;
            }
        }
    }

    if (xml.hasError())
    {
        //QMessageBox::information(NULL, QString("parseXML"), xml.errorString());
    }

    file.close();
    
    std::cout << teacherCount << " teacher" << std::endl;
    std::cout << ageCount << " ages" << std::endl;
    std::cout << sanlouCount << " 3rdFloors" << std::endl;
    std::cout << schoolCount << " schools" << std::endl;
}
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    ReadXml();
    return a.exec();
}

In the XML file, each tag tag, that is, the information in angle brackets can be obtained, and then judged.

If it is "teacher", it will count, and then you can see that there are 6 teachers in total.

The running result is shown in the figure below:

If you open the commented code in the middle, you can see each label read and print out the reading process. The running result is shown in the figure below:

Guess you like

Origin blog.csdn.net/u013015629/article/details/129464303