当前位置: 首页 > news >正文

Qt读xml文件

QXmlStreamReader

QXmlStreamReader类通过简单的流式API为我们提供了一种快速的读取xml文件的方式。他比Qt自己使用的SAX解析方式还要快。

所谓的流式读取即将一个xml文档读取成一系列标记的流,类似于SAX。而QXmlStreamReader类和SAX的主要区别就是解析这些标记的方式。使用SAX解析时,应用程序必须提供一些处理器(回调函数)来处理来自解析器的一系列的所谓的xml事件,不同的xml标记会触发不同的事件,从而进行相应的处理。而使用QXmlStreamReader,应用程序自己可以驱动整个循环,从解析器中一个接一个的拉取出xml标记。这个动作可以通过readNext()来完成,该函数会读取出下一个完整的标记,然后其tokenType()。然后,我们就可以使用一系列方便的函数,如isStartElement(),text()等来确定或得到具体所读取的内容。这种拉模式(pull)的解析方法的好处就在于,我们可以将对一个xml文档的解析分开到多个函数中来完成,对不同的标记使用一个单独的函数来处理。

QXmlStreamReader类的典型使用方法如下:

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

如果在解析的过程中出现了错误,atEnd()和hasError()会返回true,error()会返回所出现的具体错误类型。errorString(),lineNumber(),columnNumber()和characterOffset()函数可以用来得到错误的具体信息,一般我们使用这几个函数来构建一个错误字符串来提示用户具体的错误信息。同时,为了简化应用程序代码,QXmlStreamReader还提供了一个raiseError()的机制,可以让我们在必要时触发一个自定义的错误信息。

QXmlStreamReader是一个增量式的解析器。它可以处理文档不能被一下处理完的情况,比如该xml文件来自于多个文件或来自于网络。当QXmlStreamReader解析完所有的数据但该xml文档是不完整的,这时它会返回一个PrematureEndOfDocumentError类型的错误。然后,当有更多的数据到来时,它会从这个错误中恢复,然后继续调用readNext()来解析新的数据。

QXmlStreamReader是不太消耗内存的,因为它不会在内存中存储整个xml文档树,仅仅存储当前它所解析的标记。此外,QXmlStreamReader使用QStringRef来解析所有的字符串数据而不是真实的QString对象,这可以避免不必要的小字符串内存分配代价。QStringRef是对QString或其子串的一个简单包装,并提供了一些类似于QString类的API,但它不会进行内存的分配,并在底层使用了引用计数来共享数据。我们可以在需要时,调用QStringRef的toString()来得到一个真实的QString对象。

读xml文件

  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()输出当前节点的名字,这里输出labelsreadlabelsElement();      //读取labels节点的内容} else {    //raiseError()函数用来自定义输出错误日志的内容,这里输出Not a labels filereader.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();
}

读取结果如下图所示:

  1. teachers.xml:

其中,学校(school)三楼(floor3)的老师信息,还有一个学生的信息。

6个老师,1个学生

<?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>

注意:

重点在函数中的代码,移植出来可以使用。

记得将“用来计数”的变量,进行整理。

还有文件所在地址,也要更换。

将XML的文件内容,首先读取到一个变量中,再分析这个变量的内容。

受XML文件的编码格式影响很大,如果有中文乱码的现象出现,要慎重使用这种方法,可能无法读取。

#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();
}

可以得到XML文件中,各个标签tag, 即尖括号内的信息,然后进行判断。

如果是“teacher”,会计数,然后可以看到,总共有6个老师。

运行结果如下图所示:

如果把中间注释的代码打开,可以看到每一个读取到的标签,并将读取过程打印出来。运行结果如下图所示:

相关文章:

Qt读xml文件

QXmlStreamReaderQXmlStreamReader类通过简单的流式API为我们提供了一种快速的读取xml文件的方式。他比Qt自己使用的SAX解析方式还要快。所谓的流式读取即将一个xml文档读取成一系列标记的流&#xff0c;类似于SAX。而QXmlStreamReader类和SAX的主要区别就是解析这些标记的方式…...

Qt样式表

1>样式表介绍 样式表可通过 QApplication::setStyleSheet()函数将其设置到整个应用程序上&#xff0c;也可以使用 QWidget::setStyleSheet()将其设置到指定的部件或子部件上&#xff0c;不同级别均可设置样式表&#xff0c;称为样式表的层叠。样式表也可通过设计模式编辑样…...

Docker与微服务实战2022

基础篇(零基小白)1.Docker简介1.1 是什么问题&#xff1a;为什么会有docker出现&#xff1f;您要如何确保应用能够在这些环境中运行和通过质量检测&#xff1f;并且在部署过程中不出现令人头疼的版本、配置问题&#xff0c;也无需重新编写代码和进行故障修复&#xff1f; 答案就…...

Linux(传输层二)

文章目录0. 前言1. TCP协议1-1 TCP协议段格式1. TCP如何解包&#xff1f;2. TCP协议如何交付&#xff08;应用层- - 客户&#xff09;&#xff1f;3. 如何理解报文本身&#xff1f;4. 如何理解报文字段&#xff1f;1-2 确认应答(ACK)机制1-3 超时重传机制1-4 连接管理机制1. TC…...

4.Spring Cloud (Hoxton.SR8) 学习笔记—Nacos微服务治理、Nacos配置管理

本文目录如下&#xff1a;一、Nacos微服务治理Nacos 下载 与 启动Spring Cloud 集成 NacosIDEA 同一个 Application 启动多次Nacos - 配置集群Nacos - 设置负载均衡Nacos - 设置服务实例的权重二、Nacos 配置管理Nacos - 合理的把配置信息迁移到 Nacos 中Nacos - 配置命名规范N…...

卷王都在偷偷准备金三银四了...

年终奖没发&#xff1b; 简历石沉大海&#xff1b; 发消息只读不回 打开某招聘&#xff0c;看了看岗位&#xff0c;这个厂还不错&#xff0c;可是要求好高&#xff0c;我啥都不会。 “哎&#xff0c;算了&#xff0c;我简历还没更新呢&#xff0c;我躺到6月份拿到年终奖再跑…...

【C++的OpenCV】第十二课-OpenCV图像常用操作(九):找到图像的边界(轮廓)findContours()和drawContours()

&#x1f389;&#x1f389;&#x1f389;欢迎各位来到小白piao的学习空间&#xff01;\color{red}{欢迎各位来到小白piao的学习空间&#xff01;}欢迎各位来到小白piao的学习空间&#xff01;&#x1f389;&#x1f389;&#x1f389; &#x1f496;&#x1f496;&#x1f496…...

传奇开服流程—传奇单机架设教程

现在传奇私服还是那么的火爆&#xff0c;上次有报道发布站一年盈利几个亿&#xff0c;还是有很大的机会&#xff0c;很多玩家因为GM开服关服给折腾&#xff0c;刚充的钱服务器就关了&#xff0c;很是恼火&#xff0c;于是都想自己整个服开开&#xff0c;但又不知道从何下手&…...

【GoF 23】篇3:抽象工厂

1. 什么是抽象工厂&#xff1f; 提供一个创建一系列相关或互相依赖的对象接口&#xff0c;而无需指定它们的具体类。 抽象工厂是一个超级工厂&#xff0c;是其他工厂的工厂&#xff0c;或将简单工厂进一步抽象。 这样来理解&#xff1a; 我们将科技公司可以做的事情简要枚举…...

软考高级信息系统项目管理师系列之三十七:流程管理

软考高级信息系统项目管理师系列之三十七:流程管理 一、流程管理内容二、流程管理基础概念知识1.企业业务流程的整体目标2.业务流程的核心3.流程六要素4.良好的业务流程管理步骤5.企业流程管理的层次三、流程管理过程1.业务流程分析2.业务流程分析的主要方法3.业务流程分析工具…...

【WPS文字-Word】WPS文字设置段落居中对齐后公式左边右边的文字仍然无法跟公式对齐,公式和文字对不齐

一、问题背景 原来的公式左边文字是底端&#xff0c;右边文字是居中&#xff0c;我想着让左右文字全跟公式居中对齐&#xff0c;就全部设置了段落居中对齐。 结果发现&#xff0c;公式左右边的文字依然无法居中对齐。左边的文字是居中&#xff0c;但是右边的文字变成了顶端对…...

英文术语对照

underlying asset 标的资产 leverage 杠杆 forward 远期 futures 期货 options 期权 delivery 交割 broker 证券机构/经理人 CBOT 芝加哥交易所 long futures position 多头 short futures position 空头 spot price 现货价格 future price 期货价格 over-the-coun…...

CSS 扫盲

✏️作者&#xff1a;银河罐头 &#x1f4cb;系列专栏&#xff1a;JavaEE &#x1f332;“种一棵树最好的时间是十年前&#xff0c;其次是现在” 目录引入方式内部样式内联样式外部样式CSS 选择器CSS 常用属性值字体属性设置字体大小粗细文字样式文本属性文本颜色文本对齐文本装…...

【Redis黑马点评】基于session实现登录【短信验证码登录、登录验证功能、拦截器】过程详解

文章目录一. 黑马点评Redis项目实践1.1开发环境搭建1.1.1 数据库1.1.2 Springboot项目1.1.3 前端配置1.2 基于session实现登录1.2.1 发送短信验证码1.2.2 短信验证码登录1.2.3 登录验证功能1.2.3.1 编写拦截器一. 黑马点评Redis项目实践 1.1开发环境搭建 1.课程介绍&#xff…...

【C++】通过priority_queue、reverse_iterator加深对于适配器和仿函数的理解

苦尽甘来 文章目录一、仿函数&#xff08;仿函数就是一个封装()运算符重载的类&#xff09;1.C语言的函数指针2.C的仿函数对象二、priority_queue中的仿函数1.模拟实现优先级队列1.1 优先级队列的本质&#xff08;底层容器为vector的适配器&#xff09;1.2 向下调整算法建堆1.3…...

网络安全 -- 常见的攻击方式和防守

网络安全 – 常见的攻击方式和防守 一 . 网页中出现黑链 特点: 隐藏,不易发现,字体大小是0,表面上看不出来,代码层面可以查出来,也可能极限偏移,颜色一致 表现: 多表现为非法植入链接,一般点击会跳转至其他网页 例如: 1.澳门新葡京等赌博网站,获取流量,有人甚至会充钱参与赌…...

Android中实现滑动的7种方法

Android中实现滑动的7种方法前置知识Android坐标系视图坐标系触控事件---MotionEvent获取坐标的方法实现滑动的7种方法layout方法offsetLeftAndRight()和offsetTopAndBottom()LayoutParamsscrollTo和scrollByScroller属性动画ViewDragHelper参考前置知识 Android坐标系 Andro…...

【hadoop】介绍

目录 介绍 版本 优势 大数据技术生态体系 介绍 Hadoop是一个由Apache基金会所开发的分布式系统基础架构。 解决 存储和分析计算Google在大数据方面的三篇论文GFS --->HDFS Map-Reduce --->MR BigTable --->HBaseHadoop创始人Doug Cutting版本 Hadoop 三大发行版本&a…...

【C语言】有关的经典题型内含数组及递归函数题型讲解(入门适用)

C语音经典题型1. 在屏幕上输出9*9乘法口诀表2. 求10 个整数中最大值3. 计算1/1-1/21/3-1/41/5 …… 1/99 - 1/100 的值&#xff0c;打印出结果4. 编写程序数一下 1到 100 的所有整数中出现多少个数字95. 能把函数处理结果的二个数据返回给主调函数6. 实现一个函数&#xff0c;…...

MyBatis操作数据库

目录 MyBatis 功能架构 学习MyBatis 第一个MyBatis查询 1、创建数据库和表 2、搭建MyBatis开发环境 2.1、在项目中添加MyBatis框架 2.2、配置数据库连接信息 2.3、配置MyBatis中xml的保存路径&#xff08;规则&#xff09; 3、添加业务代码 3.1、创建实体类 3.2、构…...

初创团队如何借助Taotoken统一管理AI模型调用与成本

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 初创团队如何借助Taotoken统一管理AI模型调用与成本 对于资源有限的初创技术团队而言&#xff0c;在产品中集成人工智能功能已成为…...

LeRobot:开源机器人学习的终极指南 - 从零到真实世界的AI机器人控制

LeRobot&#xff1a;开源机器人学习的终极指南 - 从零到真实世界的AI机器人控制 【免费下载链接】lerobot &#x1f917; LeRobot: Making AI for Robotics more accessible with end-to-end learning 项目地址: https://gitcode.com/GitHub_Trending/le/lerobot LeRobo…...

3步打造专属桌面歌词体验:LyricsX macOS歌词神器完全指南

3步打造专属桌面歌词体验&#xff1a;LyricsX macOS歌词神器完全指南 【免费下载链接】Lyrics Swift-based iTunes plug-in to display lyrics on the desktop. 项目地址: https://gitcode.com/gh_mirrors/lyr/Lyrics LyricsX是一款专为macOS用户设计的开源桌面歌词显示…...

Jupyter Notebook插件库装完不显示?手把手教你搞定jupyter_contrib_nbextensions和configurator的正确安装顺序

Jupyter Notebook插件安装全指南&#xff1a;从原理到实战排查 第一次打开Jupyter Notebook的插件管理器&#xff0c;却发现里面空空如也——这种挫败感我太熟悉了。去年刚开始用Jupyter做数据分析时&#xff0c;我花了整整一个下午才搞明白为什么安装的插件就是不显示。后来才…...

个人开发者如何利用 Taotoken 管理多个项目的 AI 调用成本

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 个人开发者如何利用 Taotoken 管理多个项目的 AI 调用成本 对于独立开发者或自由职业者而言&#xff0c;同时维护多个小型项目是常…...

犬种识别实战:细粒度CNN模型从训练到ONNX部署

1. 项目概述&#xff1a;用一张照片&#xff0c;让模型告诉你这是什么狗 “Deep Learning (CNN) — Discover the Breed of a Dog in an Image”这个标题看起来像一句教科书里的课后习题&#xff0c;但实际落地时&#xff0c;它是一条从数据噪声里硬生生凿出来的技术路径——不…...

从Softmax到ArcFace:PyTorch实战解析人脸识别中的角度间隔损失函数

1. 从Softmax到ArcFace&#xff1a;人脸识别损失函数的进化之路 人脸识别技术如今已经深入到我们生活的方方面面&#xff0c;从手机解锁到机场安检&#xff0c;背后都离不开一个关键环节——如何让模型学会区分不同的人脸。这就像教小朋友认人一样&#xff0c;我们需要告诉模型…...

广义逆矩阵:从A+与A-的数学定义到工程求解实践

1. 广义逆矩阵&#xff1a;工程师的数学工具箱 第一次听说"广义逆矩阵"这个概念时&#xff0c;我正在处理一个推荐系统的评分预测问题。当时遇到一个头疼的情况&#xff1a;用户-物品评分矩阵极其稀疏&#xff0c;直接求逆根本行不通。导师轻描淡写地说&#xff1a;&…...

Nigate:让Mac与Windows硬盘和谐共处的开源桥梁

Nigate&#xff1a;让Mac与Windows硬盘和谐共处的开源桥梁 【免费下载链接】Free-NTFS-for-Mac Nigate: An open-source NTFS utility for Mac. It supports all Mac models (Intel and Apple Silicon), providing full read-write access, mounting, and management for NTFS …...

从零封装Cesium测量工具:我踩过的3个坑和性能优化心得(鼠标事件、坐标拾取、内存泄漏)

从零封装Cesium测量工具&#xff1a;我踩过的3个坑和性能优化心得 第一次在项目中集成Cesium测量工具时&#xff0c;我天真地以为这不过是调用几个API的简单工作。直到用户反馈地图越来越卡、测量结果偶尔出现诡异偏差时&#xff0c;我才意识到自己掉进了多少陷阱。本文将分享三…...