Qt编程进阶(95):使用QXmlStreamReader读取XML
yuyutoo 2024-10-12 01:55 4 浏览 0 评论
一、XML是什么?
XML(eXtensible Markup Language,可扩展标记语言)是普遍用于数据交换和数据存储的一种多用途文本文件格式。XML首先是由万维网协会(World Wide Web Consortium,W3C)作为SGML的一个替代品来开发的。它的语法规则与HTML相似,不过XML是一种用于语言分析的语言,它并没有要求专门的标记符,属性或者条目。HTML的XML兼容版称为XHTML。
对于比较流行的SVG(可标量化矢量图形)XML格式,QtSvg模块提供了可用于载入并呈现SVG图像的类。对于使用MathML(数学标记语言)XML格式的绘制文档,可以使用Qt Solutions中的QtMmLWidget。
对于一般的XML数据处理,Qt提供了QtXml模块,这是本文的主题。
二、XML的读取方式
QtXml模块提供了三种截然不同的应用程序编程接口用来读取XML文件:
- (1)QXmlStreamReader是一个用于读取格式良好的XML文档的快速解析器。
- (2)DOM(文档对象模型)把XML文档转换为应用程序可以遍历的树形结构。
- (3)SAX(XML简单应用程序编程接口)通过虚拟函数直接向应用程序报告“解析事件”。
QXmlStreamReader类最快且最容易使用,它同时还提供了与其他Qt兼容的应用程序编程接口。它很适用于编写单通解析器。DOM的主要优点是它能以任意顺序遍历XML文档的树形表示,同时可以实现多通解析算法。有一些应用程序甚至使用DOM树作为它们的基本数据结构。SAX则因为一些历史原因而被得以沿用至今,使用QXmlStreamReader通常会有更加简单高效的编码。
使用QXmlStreamReader是在Qt中读取XML文档的最快且最简单的方式。因为解析器的工作能力是逐渐递增的,所以它尤其适用于诸如查找XML文档中一个给定的标记符号出现的次数、读取内存容纳不了的特大文件、组装定制的数据结构以反映XML文档的内容等。
QXmlStreamReader解析器根据下图中所列出的记号工作。每次只要调用readNext()函数,下一个记号就会被读取并变成当前的记号。当前记号的属性取决于记号的类型,可以使用表格中列出的getter函数读取当前记号。
考虑如下的XML文档:
<doc>
<quote>Einmal ist keinmal</quote>
</doc>
如果解析这个文档,则readNext()每调用一次都将生成一个新记号,若使用getter函数还会获得额外的信息;
StartDocument
StartElement (name() == "doc")
StartElement (name() == "quote")
Characters (text() == " Einmal ist keinmal")
EndElement (name() == "quote")
EndElement (name() == "doc")
EndDocument
每次调用readNext()后,都可以使用isStartElement(),isCharacters()及类似的函数或者仅仅用state()来测试当前记号的类型。
三、QXmlStreamReader读取XML实例
下面将查看一个实例,它告诉我们如何使用QXmlStreamReader解析一个专门的XML文件格式(图1的bookindex.xml)并在QTreeWidget中显示其内容。所解析的是那种具有书刊索引目录且包含索引条目和子条目的文档格式。下图是在QTreeWidget中显示的书刊索引文件。
1、QtXml库
在这个应用程序中使用的QXmlStreamReader类是QtXml库中的一部分。必须在.pro文件中加入如下一行命令:
QT += xml
在代码中添加头文件:
#include <QXmlStreamReader>
2、主程序代码
首先查看应用程序中XML阅读器在上下文中是如何使用的。
Widget::Widget(QWidget *parent)
: QWidget(parent), ui(new Ui::Widget)
{
ui->setupUi(this);
ui->treeWidget->setColumnCount(2);
QStringList header;
header<<"Terms"<<"Pages";
ui->treeWidget->setHeaderLabels(header);
XmlStreamReader reader(ui->treeWidget);
reader.readFile("bookindex.xml");
}
在图中显示的应用程序界面中创建一个QTreeWidget。之后,这个应用程序创建一个XmlStreamReader,并将树形窗口部件值传递给该XmlStreamReader,并要求它解析所指定的一个文件。
3、XmlStreamReader类的定义
然后,我们将查看阅读器的实现代码。
class XmlStreamReader
{
public:
XmlStreamReader(QTreeWidget *tree);
bool readFile(const QString &fileName);
private:
void readBookindexElement();
void readEntryElement(QTreeWidgetItem *parent);
void readPageElement(QTreeWidgetItem *parent);
void skipUnknownElement();
QTreeWidget *treeWidget;
QXmlStreamReader reader;
};
XmlStreamReader类提供了两个公共函数:构造函数和readFile()函数。这个类使用QXmlStreamReader解析XML文件,并配合QTreeWidget窗口以反映其读入的XML数据。通过使用向下递归的方法来实现这一解析过程。
- readBookindexElement()解析一个含有0或0个以上<entry>元素的<bookindex>…</bookindex>元素。
- readEntryElemen()解析一个含有0或0个以上<page>元素的<entry>…</entry>元素,以及嵌套任意层次的含有0或0个以上<entry>元素。
- readPageElement()解析一个<page> …</page>元素。
- skipUnknownElement()跳过不能识别的元素。
4、XmlStreamReader类的实现
现在看看XmlStreamReader类的实现,由构造函数开始。
XmlStreamReader::XmlStreamReader(QTreeWidget *tree)
{
treeWidget=tree;
}
构造函数只是用来建立阅读器将使用的那个QTreeWidget。所有的操作都将在readFile()函数中完成(由main()函数调用)。
bool XmlStreamReader::readFile(const QString &fileName)
{
QFile file(fileName);
if(!file.open(QFile::ReadOnly | QFile::Text)){ // (a)
std::cerr<<"Error: Cannot read file "<<qPrintable(fileName)
<<": "<<qPrintable(file.errorString())<<std::endl;
return false;
}
reader.setDevice(&file);
reader.readNext(); // (b)
while (!reader.atEnd()) {
if(reader.isStartElement()){
if(reader.name()=="bookindex"){ // (c)
readBookindexElement();
}else{
reader.raiseError(QObject::tr("Not a bookindex file"));
}
}else{
reader.readNext();
}
}
file.close();
if(reader.hasError()){ // (d)
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;
}
其中:
- (a):readFile()函数首先会尝试打开文件。如果失败,则会输出一条出错信息并返回false值;如果成功,则它将被设置为QXmlStreamReader的输人设备。
- (b):QXmlStreamReader的readNext()函数从输人流中读取下一个记号。如果成功而且还没有到达XML文件的结尾,函数将进人循环。由于索引文件的结构,我们知道在该循环内部只有三种可能性发生:<bookindex>开始标签正好被读入;另一个开始标签正好被读入(在这种情况下,读取的文件不是一个书刊索引);读入的是其他种类的记号。
- (c):如果有正确的开始标签,就调用readBookindexElement()继续完成处理。否则,就调用QXmlStreamReader::raiseError()并给出出错信息。下一次(在while循环条件下)调用atEnd()时,它将返还true值。这就确保了解析过程可以在遇到错误时能尽快停止。通过对QFile调用error()和errorString(),就可以在稍后查询这些出错信息。当在书刊索引文件中检测到有错误时,也会立即返回一个类似的出错信息。其实,使用raiseError()通常会更加方便,因为它对低级的XML解析错误和与应用程序相关的错误使用了相同的错误报告机制,而这些低级的XML解析错误会在QXmlStreamReader运行到无效的XML时就自动出现。
- (d):一旦处理完成,就会关闭文件,如果存在解析器错误或者文件错误,该函数就输出一个出错信息并返回false值;否则,返回true值并报告解析成功。
void XmlStreamReader::readBookindexElement()
{
reader.readNext();
while(!reader.atEnd()){
if(reader.isEndElement()){
reader.readNext();
break;
}
if(reader.isStartElement()){
if(reader.name()=="entry"){
readEntryElement(treeWidget->invisibleRootItem());
}else{
skipUnknownElement();
}
}else{
reader.readNext();
}
}
}
readBookindexElement()的作用就是读取文件的主体部分。它首先跳过当前的记号(此处只可能是<bookindex>开始标签),然后遍历读取整个输人文件。
如果读取到了关闭标签,那么它只可能是</bookindex>标签,否则QXmlStreamReader早就已经报告出错。如果是那样的话,就跳过这个标签并跳出循环。否则将应该有一个顶级索引<entry>开始标签。如果情况确实如此,调用readEntryElement()来处理条目数据;不然,就调用skipUnknownElement()。使用skipUnknownElement()而不调用raiseError(),意味着如果要在将来扩展书刊索引格式以包含新的标签的话这个阅读器将继续存效,因为它仅忽略了不能识别的标签。
readEntryElement()具有一个确认父对象条目的QTreeWidgetItem *参数。我们将QTreeWidget::invisibleRootItem()作为父对象项传递,以使新的项以其为根基。在readEntryElement()中,用一个不同的父对象项循环调用readEntryElement()。
void XmlStreamReader::readEntryElement(QTreeWidgetItem *parent)
{
QTreeWidgetItem *item = new QTreeWidgetItem(parent);
item->setText(0,reader.attributes().value("term").toString());
reader.readNext();
while(!reader.atEnd()) {
if (reader.isEndElement()) {
reader.readNext();
break;
}
if (reader.isStartElement()) {
if (reader.name() == "entry") {
readEntryElement(item);
} else if (reader.name() == "page") {
readPageElement(item);
} else {
skipUnknownElement();
}
} else {
reader.readNext();
}
}
}
每当遇到一个<entry>开始标签时,就会调用readEntryElement()函数。我们希望为每一个索引条目创建一个树形的窗口部件项,因此创建一个新的QTreeWidgetltem,并将其第一列的文本值设置为条目的项属性文本。
一旦条目被添加到树中,就开始读取下一个记号。如果这是一个关闭标签,就跳过该标签并跳出循环。如果遇到的是开始标签,那么它可能是<entry>标签(表示一个子条目),<page>标签 (该条目项的页码数),或者是一个未知的标签。如果开始标签是一个子条,就递归调用readEntryElement()。如果该标签是<page>标签,就调用readPageElement()。
void XmlStreamReader::readPageElement(QTreeWidgetItem *parent)
{
QString page = reader.readElementText();
if (reader.isEndElement())
reader.readNext();
QString allPages = parent->text(1);
if (! allPages.isEmpty())
allPages += ", ";
allPages += page;
parent->setText(1, allPages);
}
只要读取的是<page>标签,就调用readPageElement()函数。被传递的正是符合页码文本所属条目的树项。我们从读取<page>和</page>标签之间的文本开始。成功读取完以后,readElementText()函数将让解析器停留在必须跳过的</page>标签上。
这些页被存储在树形窗口部件项的第二列。我们首先提取那里已有的文本。如果文本不为空值,就在其后添加一个逗号,为新页的文本做好淮备。然后,添加新的文本并相应地更新该列的文本。
void XmlStreamReader::skipUnknownElement()
{
reader.readNext();
while (!reader.atEnd()) {
if (reader.isEndElement()) {
reader.readNext();
break;
}
if (reader.isStartElement()) {
skipUnknownElement();
} else {
reader.readNext();
}
}
}
最后,当遇到未知的标签时,将继续读取,直到读取到也将跳过的未知元素的关闭标签为止。这意味着我们将跳过那些具有良好形式但却无法识別的元素,并从XML文件中读取尽可能多的可识別的数据。
——————————————————
对于本文实例完整代码有需要的朋友,可关注并在评论区留言!
相关推荐
- 网站制作的流程是什么呢?简单大概的流程
-
关注我!了解更多网站建设的小干货~如今,随着网络时代的全面到来,网站在人们的生活和工作中发挥着极其重要的作用。网站制作的发展使更多的人加入了这个行业。如果你想掌握网站制作的知识,你可以在学校或网上学习...
- 一款谷歌(Google)打造的广告网页设计制作软件
-
GoogleWebDesigner是由谷歌(Google)打造的一款广告网页设计制作软件,它能够帮助从事于广告网页设计工作或是有这方面需求的用户更加有效快速的进行完成相关的行业设计工作,软件可以支...
- 普通网站如何制作一个网站?
-
对行外人来讲,在预备做一个网站项目时,最想了解的无非就是网站制作的悉数流程。网站制作是要有计划的,事先策划好才能更快更好的完成。网站的几个基本组成元素:域名+空间+程序+模板+维护经验+日常管理.网站...
- 用纯Python就能写一个漂亮的网页,再见HTML
-
再见HTML!用纯Python就能写一个漂亮的网页我们在写一个网站或者一个网页界面的时候,需要学习很多东西,对小白来说很困难!比如我要做一个简单的网页交互:天啊,听听头都大呢!其实我就给老板做一个...
- HTML表单4(form的action、method属性)——零基础自学网页制作
-
表单的工作过程表单的信息发送与处理过程可以简单的进行图示,如下图。以注册会员为例,用户在自己的电脑上打开相应的注册表单页面填写信息,完成填写后点击提交按钮,也就是图中1所示过程。这时浏览器会将这些信息...
- 官网网站设计网页制作模板建站前端自适应响应式网站仿站门户
-
案例背景航科慧联无人机搜索雷达能够在多种天气下检测到无人机的入侵、并获得目标的距离、方向和高度等具体信息,是无人机反制作战中的关键设备。航科慧联无人机搜索雷达能够在多种天气下检测到无人机的入侵、并获得...
- 软网推荐:在线制作软件图标
-
在制作PPT演示、软件、网页或其他程序时,我们往往需要用到一些个性化的图标。现在,即便是不安装任何软件,也可以上网在线制作自己需要的图标。首先访问如下制作网址:http://www.rw-design...
- 自定义跳转的h5网页如何制作?
-
文章来源:墨鹊微站...
- 网页如何制作?这几点要知道
-
这是一个个性张扬的时代,也是一个动手能力和动脑能力都比较强的时代,因此很多人对于能够自己动手完成的东西,都不太想假手于人。于是网页制作成了各大搜索引擎里面排名比较靠前的关键词之一。想要知道网页如何制作...
- 手机端网站简单制作教程,怎么快速制作一个移动端的网站
-
想要创建一个手机端的网站,需要有域名、已经完成网站页面的开发设计,零基础朋友不懂代码技术,直接在线套用乔拓云里面的网站模板来开发是比较简单可行的,进入乔拓云网,复制网站模板编辑网站的内容,注册域名后绑...
- 几张动图教你轻松了解Dreamweaver做网页
-
施老师:当今可是互联网时代,人们的生活、社交离不开互联网,那么不管你是网页设计师,还是销售达人,还是个体户,总必不可少的要在网上呈现一些页面给客户看,这个就是让你做网页,而Dreamweaver是做网...
- 用Deepseek制作网页版的汉诺塔游戏保姆级教程
-
在deepseek中输入:“帮我做一个网页版的汉诺塔演示游戏,游戏包含2层、3层、4层、5层的汉诺塔游戏演示,制作自动求解演示按钮,点击按钮就可以生成出步数,同时自动演示最优解动画。”...
- JS制作网页版计算器
-
大家晚上好,我是洁哥,抱歉今天有点晚了,但是洁哥不会缺席哦,今天我们来看一个JS实现网页版计算器的例题,先来看一看出来的效果吧(123+123=246)(123-123=0)(123*123=1512...
- 网页制作流程哪几步
-
在数字化时代,网页制作成为企业和个人展示形象、传递信息的重要方式。但是,许多人对于网页制作的流程仍感到困扰。为了解决这一问题,我们将深入探讨网页制作的关键步骤,助您更好地理解和应用这一过程。第一步:需...
- 这4个设计技巧,教你做好个人网页制作
-
随着互联网发展,个人建站已经不是什么稀奇事,学生、求职者、插画师、摄影师、作家……都可以制作个人网站,用来展示自身形象,或者吸引粉丝。那么如何做好个人网站呢?在不懂设计和技术知识的情况下,个人网页制作...
你 发表评论:
欢迎- 一周热门
-
-
前端面试:iframe 的优缺点? iframe有那些缺点
-
带斜线的表头制作好了,如何填充内容?这几种方法你更喜欢哪个?
-
漫学笔记之PHP.ini常用的配置信息
-
其实模版网站在开发工作中很重要,推荐几个参考站给大家
-
推荐7个模板代码和其他游戏源码下载的网址
-
[干货] JAVA - JVM - 2 内存两分 [干货]+java+-+jvm+-+2+内存两分吗
-
正在学习使用python搭建自动化测试框架?这个系统包你可能会用到
-
织梦(Dedecms)建站教程 织梦建站详细步骤
-
【开源分享】2024PHP在线客服系统源码(搭建教程+终身使用)
-
2024PHP在线客服系统源码+完全开源 带详细搭建教程
-
- 最近发表
- 标签列表
-
- mybatis plus (70)
- scheduledtask (71)
- css滚动条 (60)
- java学生成绩管理系统 (59)
- 结构体数组 (69)
- databasemetadata (64)
- javastatic (68)
- jsp实用教程 (53)
- fontawesome (57)
- widget开发 (57)
- vb net教程 (62)
- hibernate 教程 (63)
- case语句 (57)
- svn连接 (74)
- directoryindex (69)
- session timeout (58)
- textbox换行 (67)
- extension_dir (64)
- linearlayout (58)
- vba高级教程 (75)
- iframe用法 (58)
- sqlparameter (59)
- trim函数 (59)
- flex布局 (63)
- contextloaderlistener (56)