Qt解析XML的三種常見(jiàn)方法實(shí)現(xiàn)與比較
一、XML概述
XML簡(jiǎn)介
XML(eXtensible Markup Language)是一種可擴(kuò)展的標(biāo)記語(yǔ)言,用于存儲(chǔ)和傳輸結(jié)構(gòu)化數(shù)據(jù)。它被設(shè)計(jì)為兼具人類(lèi)可讀性和機(jī)器可讀性,廣泛應(yīng)用于配置、數(shù)據(jù)交換和Web服務(wù)等領(lǐng)域。
XML核心特性
可擴(kuò)展性:允許用戶(hù)自定義標(biāo)簽和數(shù)據(jù)結(jié)構(gòu)。
平臺(tái)無(wú)關(guān)性:獨(dú)立于編程語(yǔ)言和操作系統(tǒng)。
自描述性:通過(guò)標(biāo)簽和屬性明確描述數(shù)據(jù)內(nèi)容。
XML基本結(jié)構(gòu)
XML文檔由以下部分組成:
- 聲明:定義XML版本和編碼(如<?xml version="1.0" encoding="UTF-8"?>)。
- 根元素:文檔的唯一頂層元素(如<root>)。
- 子元素與屬性:嵌套的標(biāo)簽和鍵值對(duì)(如<book id="1">)。
示例代碼:
<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
<book id="101">
<title>XML Basics</title>
<author>John Doe</author>
</book>
</bookstore>
XML與HTML的區(qū)別
目的:HTML用于數(shù)據(jù)展示,XML用于數(shù)據(jù)存儲(chǔ)和傳輸。
標(biāo)簽:HTML標(biāo)簽預(yù)定義,XML標(biāo)簽可自定義。
嚴(yán)格性:XML對(duì)語(yǔ)法要求更嚴(yán)格(如必須閉合標(biāo)簽)。
XML相關(guān)技術(shù)
DTD/XSD:定義文檔結(jié)構(gòu)和驗(yàn)證規(guī)則。
XPath/XQuery:用于XML數(shù)據(jù)查詢(xún)。
XSLT:將XML轉(zhuǎn)換為其他格式(如HTML)。
XML因其靈活性和跨平臺(tái)特性,至今仍在Web服務(wù)、配置文件(如Android布局)和數(shù)據(jù)交換(如SOAP協(xié)議)中廣泛應(yīng)用。
二、Qt中的XML解析方法
1. SAX解析(事件驅(qū)動(dòng)模型)
特點(diǎn):
- 基于事件的解析方式
- 不需要將整個(gè)文檔加載到內(nèi)存
- 順序讀取文檔,遇到元素時(shí)觸發(fā)事件
- 內(nèi)存效率高,適合大型XML文件
理解Qt的SAX解析機(jī)制
Qt中的SAX(Simple API for XML)解析是一種基于事件驅(qū)動(dòng)的XML解析方式。與DOM解析不同,SAX解析不將整個(gè)XML文檔加載到內(nèi)存,而是逐行讀取并觸發(fā)相應(yīng)的事件。這種方式適合處理大型XML文件,內(nèi)存占用較低。
實(shí)現(xiàn)SAX解析的核心類(lèi)
Qt中SAX解析主要依賴(lài)QXmlSimpleReader和QXmlDefaultHandler類(lèi)。QXmlSimpleReader負(fù)責(zé)讀取XML文檔并觸發(fā)事件,QXmlDefaultHandler則提供處理這些事件的虛函數(shù),用戶(hù)需繼承此類(lèi)并重寫(xiě)關(guān)鍵方法。
#include <QXmlDefaultHandler> #include <QXmlSimpleReader> #include <QFile>
自定義處理器類(lèi)
創(chuàng)建一個(gè)繼承自QXmlDefaultHandler的子類(lèi),重寫(xiě)以下常用方法:
class MyXmlHandler : public QXmlDefaultHandler {
public:
bool startElement(const QString &namespaceURI,
const QString &localName,
const QString &qName,
const QXmlAttributes &attrs) override {
// 處理元素開(kāi)始標(biāo)簽
return true;
}
bool endElement(const QString &namespaceURI,
const QString &localName,
const QString &qName) override {
// 處理元素結(jié)束標(biāo)簽
return true;
}
bool characters(const QString &ch) override {
// 處理元素文本內(nèi)容
return true;
}
bool fatalError(const QXmlParseException &exception) override {
// 處理錯(cuò)誤
return false;
}
};
配置解析流程
建立完整的SAX解析流程需要以下步驟:
QFile file("example.xml");
if (!file.open(QFile::ReadOnly | QFile::Text)) {
qDebug() << "Cannot open file";
return;
}
QXmlInputSource source(&file);
QXmlSimpleReader reader;
MyXmlHandler handler;
reader.setContentHandler(&handler);
reader.setErrorHandler(&handler);
???????bool ok = reader.parse(source);
if (!ok) {
qDebug() << "Parsing failed";
}
file.close();處理元素屬性
在startElement方法中可以通過(guò)QXmlAttributes參數(shù)獲取元素屬性:
bool startElement(..., const QXmlAttributes &attrs) {
for (int i = 0; i < attrs.count(); ++i) {
QString name = attrs.qName(i);
QString value = attrs.value(i);
// 處理屬性
}
return true;
}
錯(cuò)誤處理機(jī)制
SAX解析過(guò)程中可能出現(xiàn)格式錯(cuò)誤,通過(guò)重寫(xiě)fatalError方法可以捕獲這些異常:
bool fatalError(const QXmlParseException &exception) {
qDebug() << "Error at line" << exception.lineNumber()
<< ", column" << exception.columnNumber()
<< ":" << exception.message();
return false; // 停止解析
}
性能優(yōu)化建議
對(duì)于大型XML文件,可以考慮以下優(yōu)化措施:
- 避免在事件處理函數(shù)中進(jìn)行復(fù)雜計(jì)算
- 使用QStringRef代替QString處理文本內(nèi)容
- 及時(shí)釋放不再需要的資源
這種解析方式適合需要高效處理XML數(shù)據(jù)流的場(chǎng)景,如配置文件讀取、網(wǎng)絡(luò)數(shù)據(jù)傳輸解析等。
2. DOM解析(樹(shù)形模型)
特點(diǎn):
- 將整個(gè)XML文檔加載到內(nèi)存形成樹(shù)狀結(jié)構(gòu)
- 可以隨機(jī)訪(fǎng)問(wèn)文檔的任何部分
- 內(nèi)存消耗較大,適合小型XML文件
- 支持修改XML文檔
Qt DOM解析XML的基本方法
Qt提供QDomDocument類(lèi)用于DOM方式解析XML。DOM將XML文檔作為樹(shù)結(jié)構(gòu)處理,允許隨機(jī)訪(fǎng)問(wèn)節(jié)點(diǎn),適合小型XML文件或需要頻繁修改的場(chǎng)景。
創(chuàng)建QDomDocument對(duì)象
QDomDocument doc;
QFile file("example.xml");
if (!file.open(QIODevice::ReadOnly)) return;
if (!doc.setContent(&file)) {
file.close();
return;
}
file.close();
獲取根元素
QDomElement root = doc.documentElement();
if (root.isNull()) {
// 處理空文檔情況
}
遍歷子節(jié)點(diǎn)
QDomNode node = root.firstChild();
while (!node.isNull()) {
if (node.isElement()) {
QDomElement element = node.toElement();
QString tagName = element.tagName();
QString text = element.text();
}
node = node.nextSibling();
}
讀取元素屬性
QString attrValue = element.attribute("attributeName");
QString attrValue2 = element.attribute("attributeName2", "defaultValue");
創(chuàng)建新XML文檔
QDomDocument newDoc;
QDomElement newRoot = newDoc.createElement("root");
newDoc.appendChild(newRoot);
QDomElement child = newDoc.createElement("child");
child.setAttribute("id", "1");
newRoot.appendChild(child);
寫(xiě)入XML文件
QFile outputFile("output.xml");
if (outputFile.open(QIODevice::WriteOnly)) {
QTextStream stream(&outputFile);
doc.save(stream, 4); // 4表示縮進(jìn)空格數(shù)
outputFile.close();
}
處理XML命名空間
QDomElement nsElement = doc.createElementNS("http://example.com/ns", "prefix:element");
錯(cuò)誤處理
QString errorMsg;
int errorLine, errorColumn;
if (!doc.setContent(&file, &errorMsg, &errorLine, &errorColumn)) {
qDebug() << "Error at line" << errorLine << "column" << errorColumn << ":" << errorMsg;
}
DOM解析方式會(huì)一次性加載整個(gè)XML文檔到內(nèi)存,對(duì)于大型XML文件可能導(dǎo)致性能問(wèn)題。這種情況下可以考慮使用Qt的SAX(QXmlSimpleReader)或流式解析(QXmlStreamReader)方式。
3. QXmlStreamReader(流式解析)
特點(diǎn):
- Qt Core模塊提供的現(xiàn)代解析器
- 結(jié)合SAX和DOM的優(yōu)點(diǎn)
- 類(lèi)似SAX的事件驅(qū)動(dòng),但API更簡(jiǎn)單
- 不需要將整個(gè)文檔加載到內(nèi)存
- 支持讀取和寫(xiě)入
QXmlStreamReader 簡(jiǎn)介
QXmlStreamReader 是 Qt 提供的流式 XML 解析器,用于高效讀取 XML 數(shù)據(jù)。與 DOM 解析器不同,它逐元素解析,內(nèi)存占用低,適合處理大型 XML 文件。
基本使用方法
初始化與輸入設(shè)置
QXmlStreamReader reader;
reader.addData(xmlData); // 輸入 XML 數(shù)據(jù)(QString 或 QByteArray)
// 或通過(guò)文件輸入
QFile file("example.xml");
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
return;
reader.setDevice(&file);
解析流程
通過(guò)循環(huán)檢查 reader.readNext() 或直接處理 reader.tokenType() 來(lái)遍歷 XML:
while (!reader.atEnd()) {
QXmlStreamReader::TokenType type = reader.readNext();
if (type == QXmlStreamReader::StartElement) {
QString elementName = reader.name().toString();
if (elementName == "book") {
QString id = reader.attributes().value("id").toString();
}
} else if (type == QXmlStreamReader::Characters) {
QString text = reader.text().toString().trimmed();
if (!text.isEmpty()) {
// 處理文本內(nèi)容
}
}
}
if (reader.hasError()) {
qDebug() << "Error:" << reader.errorString();
}關(guān)鍵方法與屬性
常用 TokenType 類(lèi)型
- StartElement:開(kāi)始標(biāo)簽(如 <book>)。
- EndElement:結(jié)束標(biāo)簽(如 </book>)。
- Characters:標(biāo)簽之間的文本內(nèi)容。
- Attribute:標(biāo)簽的屬性(通過(guò) attributes() 訪(fǎng)問(wèn))。
屬性與內(nèi)容獲取
// 獲取當(dāng)前元素名
QString name = reader.name().toString();
// 獲取屬性
QXmlStreamAttributes attrs = reader.attributes();
QString value = attrs.value("attrName").toString();
// 獲取文本內(nèi)容(需在 Characters 類(lèi)型時(shí)處理)
QString text = reader.text().toString();
錯(cuò)誤處理
檢查解析錯(cuò)誤并獲取詳細(xì)信息:
if (reader.hasError()) {
qDebug() << "Line:" << reader.lineNumber();
qDebug() << "Column:" << reader.columnNumber();
qDebug() << "Error:" << reader.errorString();
}
示例:解析嵌套 XML
假設(shè) XML 結(jié)構(gòu)如下:
<library>
<book id="1">
<title>Qt Guide</title>
<author>Alice</author>
</book>
</library>
解析代碼片段:
while (!reader.atEnd()) {
reader.readNext();
if (reader.isStartElement()) {
if (reader.name() == "book") {
QString id = reader.attributes().value("id").toString();
} else if (reader.name() == "title") {
QString title = reader.readElementText(); // 直接讀取文本
} else if (reader.name() == "author") {
QString author = reader.readElementText();
}
}
}
性能優(yōu)化建議
避免頻繁字符串轉(zhuǎn)換:reader.name() 返回 QStringView,可直接用于比較。
跳過(guò)無(wú)關(guān)內(nèi)容:使用 reader.skipCurrentElement() 跳過(guò)不需要的嵌套元素。
重用讀取器:清除狀態(tài)后復(fù)用 QXmlStreamReader 對(duì)象以減少開(kāi)銷(xiāo)。
通過(guò)流式解析,QXmlStreamReader 在性能和內(nèi)存效率上表現(xiàn)優(yōu)異,適合處理大型或?qū)崟r(shí) XML 數(shù)據(jù)流。
三、實(shí)例展示
解析下面xml內(nèi)容:
<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
<book category="fiction">
<title lang="en">Harry Potter</title>
<author>J.K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
<book category="non-fiction">
<title lang="en">The Great Gatsby</title>
<author>F. Scott Fitzgerald</author>
<year>1925</year>
<price>19.99</price>
</book>
</bookstore>解析代碼:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QFile>
#include <QXmlStreamReader>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void parseWithStreamReader(const QString &fileName)
{
QFile file(fileName);
if(!file.open(QIODevice::ReadOnly | QIODevice::Text))
{
// 處理錯(cuò)誤
qDebug()<<"file open error";
return;
}
QXmlStreamReader xml(&file);
while (!xml.atEnd() && !xml.hasError())
{
QXmlStreamReader::TokenType token = xml.readNext();
if (token == QXmlStreamReader::StartDocument)
{
qDebug() << "Start Document";
}
else if(token==QXmlStreamReader::StartElement)
{
qDebug() << "Start Element: " << xml.name().toString();
// 處理不同的元素
if (xml.name().toString() == "book") {
qDebug() << "Category: " << xml.attributes().value("category").toString();
} else if (xml.name().toString() == "title") {
qDebug() << "Title: " << xml.readElementText();
} else if (xml.name().toString() == "author") {
qDebug() << "Author: " << xml.readElementText();
} else if (xml.name().toString() == "year") {
qDebug() << "Year: " << xml.readElementText().toInt();
} else if (xml.name().toString() == "price") {
qDebug() << "Price: " << xml.readElementText().toDouble();
}
}
else if (token == QXmlStreamReader::EndElement)
{
qDebug() << "End Element: " << xml.name().toString();
}
}
if (xml.hasError())
{
qDebug()<<"xml error!";
}
file.close();
}
void MainWindow::on_pushButton_clicked()
{
parseWithStreamReader("test.xml");
}效果展示:

到此這篇關(guān)于Qt解析XML的三種常見(jiàn)方法實(shí)現(xiàn)與比較的文章就介紹到這了,更多相關(guān)Qt解析XML內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++中strlen函數(shù)的三種實(shí)現(xiàn)方法
在C語(yǔ)言中我們要獲取字符串的長(zhǎng)度,可以使用strlen?函數(shù),strlen?函數(shù)計(jì)算字符串的長(zhǎng)度時(shí),直到空結(jié)束字符,但不包括空結(jié)束字符,因?yàn)閟trlen函數(shù)時(shí)不包含最后的結(jié)束字符的,因此一般使用strlen函數(shù)計(jì)算的字符串的長(zhǎng)度會(huì)比使用sizeof計(jì)算的字符串的字節(jié)數(shù)要小2022-05-05
C語(yǔ)言編程之掃雷小游戲空白展開(kāi)算法優(yōu)化
掃雷是電腦上很經(jīng)典的游戲,特意去網(wǎng)上玩了一會(huì),幾次調(diào)試之后,發(fā)現(xiàn)這個(gè)比三子棋要復(fù)雜一些,尤其是空白展開(kāi)算法上和堵截玩家有的一拼,與實(shí)際游戲差別較大,不能使用光標(biāo),下面來(lái)詳解每一步分析2021-09-09
C++簡(jiǎn)明分析講解引用與函數(shù)提高及重載
今天繼續(xù)開(kāi)始對(duì)C++核心編程知識(shí)的分享與系統(tǒng)講解,第一,這里會(huì)提到“引用”方法傳參以及剖析引用的本質(zhì);第二,我們對(duì)函數(shù)來(lái)一個(gè)提高,相當(dāng)于進(jìn)階函數(shù)了,包括函數(shù)的默認(rèn)值,簡(jiǎn)單的提一下函數(shù)的占位參數(shù),函數(shù)重載以及注意事項(xiàng),接下來(lái)上正文2022-05-05
vs2017智能感知錯(cuò)誤解決代碼標(biāo)紅但編譯通過(guò)問(wèn)題
這篇文章主要介紹了vs2017智能感知錯(cuò)誤代碼標(biāo)紅但編譯通過(guò)問(wèn)題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08

