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 API s 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
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(); //Read the label tag void readlabelElement(); //Read the label tag void readxElement(); //read x-label void readyElement(); //read y-label void skipUnknownElement(); //skip unknown tags QXmlStreamReader reader; }; #endif // XMLREAGER_H
#include "xmlreader.h" #include <iostream> #include <QDebug> #include <QFile> xmlreader::xmlreader() { } bool xmlreader::readFile(const QString &fileName) { //Open the file in read-only and text mode. If the opening fails, output an error log and return 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; } //Set the file as the input device of the xml reader reader.setDevice(&file); reader.readNext(); //Read the next node directly, because the first tag read is the head of the XML file (the first line) while (!reader.atEnd()) //The outer loop reads in a loop until the end of the file is reached { if (reader.isStartElement()) //Outer branch, if it is not the starting label, directly read the next node { if (reader.name() == "labels") //For internal branches, if the root node is not == labels, //Indicates that the file read is wrong { qDebug() << reader.name();//Output the name of the current node through qDebug(), here output labels readlabelsElement(); //Read the contents of the labels node } else { //The raiseError() function is used to customize the content of the output error log, here output Not a labels file reader.raiseError(QObject::tr("Not a labels file")); } } else { reader.readNext(); } } //Close the file, if there is an error in reading (hasError()) or the file has an error, output an error message and return 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();//After reading the root node labels, continue to read the next node while (!reader.atEnd()) { if (reader.isEndElement()) { reader.readNext(); break; //If it is an end node, end the loop //The loop is executed, and the first end node read is </labels>, not </label>; //This is the result of executing the readlabelElement() function. When </label> is read, //This function breaks out of the loop and reads the next node, and the next node is <label> or </labels> } if (reader.isStartElement()) { if (reader.name() == "label") { //Get the attributes() value of the label, that is, the id, and convert it into a string output qDebug() << reader.attributes().value("id").toString(); qDebug() << reader.name(); readlabelElement(); } else { skipUnknownElement();//Unknown nodes skip directly } } 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();//After executing this function, y gets the coordinate value, and the current node //Automatically becomes an end node</y> qDebug() << "y:" << y; if (reader.isEndElement()) reader.readNext(); //Here, read the next node, which is </label> } //is a recursive function void xmlreader::skipUnknownElement() { reader.readNext(); while (!reader.atEnd()) { if (reader.isEndElement()) { reader.readNext(); break; } if (reader.isStartElement()) { skipUnknownElement();//recursive call of function } 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:

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() { //used to count int teacherCount = 0; int ageCount = 0; int sanlouCount = 0; int schoolCount = 0; //read file QString fileName = "D:/JBXML/teachers.xml"; QFile file(fileName); if (!file.open(QFile::ReadOnly | QFile::Text)) { return ; } //QXmlStreamReader operates on any QIODevice. QXmlStreamReader xml(&file); //Parses the XML until the end while (!xml.atEnd() && !xml.hasError()) { //Read the next element. QXmlStreamReader::TokenType token = xml.readNext(); /*The following content is used to analyze the read content, and each read label name can be printed out*//* if (token == QXmlStreamReader::Invalid) { //If there is a read error, it will be printed std::cout << xml.errorString().toStdString(); } std::cout << xml.tokenString().toStdString() << "\t"; std::cout << xml.name().toString().toStdString() << std::endl;*/ /*Display this analysis process, you will see a very clear reading process*/ //If only StartDocument is obtained, proceed to the next if (token == QXmlStreamReader::StartDocument) { continue; } //If StartElement is acquired, try to read if (token == QXmlStreamReader::StartElement) { //If it is a person, it is parsed 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:
