Qt读xml文件

news2024/11/24 8:05:01

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()输出当前节点的名字,这里输出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();
}

读取结果如下图所示:

  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个老师。

运行结果如下图所示:

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/403834.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux自动化交互命令expect测试

介绍 expect 是由Don Libes基于Tcl&#xff08;Tool Command Language &#xff09;语言开发的&#xff0c;主要应用于自动化交互式操作的场景&#xff0c;借助Expect处理交互的命令&#xff0c;可以将交互过程如&#xff1a;ssh登录&#xff0c;ftp登录等写在一个脚本上&#…

RabbitMQ系列(1)--RabbitMQ简介

1、RabbitMQ概念RabbitMQ是一个消息中间件&#xff0c;不对消息进行处理&#xff0c;只对消息做接收、存储和转发。2、RabbitMQ四大核心概念(1)生产者产生数据发送信息的程序(2)交换机交换机是RabbitMQ中一个非常重要的部件&#xff0c;接收来着生产者的消息并把消息推送到队列…

PMP项目管理项目沟通管理

目录1 项目沟通管理2 规划沟通管理3 管理沟通4 监督沟通1 项目沟通管理 项目沟通管理包括通过开发工件&#xff0c;以及执行用于有效交换信息的各种活动&#xff0c;来确保项目及其相关方的信息需求得以满足的各个过程。项目沟通管理由两个部分组成&#xff1a;第一部分是制定…

云企业网CEN介绍与实践

云企业网CEN介绍 云企业网&#xff08;Cloud Enterprise Network&#xff09;是一款能快速构建混合云和分布式业务系统的全球网络服务。 运行在云厂商的私有全球网络上实现跨地域专有网络间&#xff0c;专有网络与本地数据中心间的私网通信 提供高效、稳定的网络传输服务适用…

【网络】什么是RPC?RPC与HTTP有什么关系?

文章目录RPC是什么RPC和HTTP的关系和区别[附]关于REST论文中提到的"HTTP不是RPC"重点参考 凤凰架构-远程过程调用 既然有HTTP为什么还要有RPC&#xff1f; RPC是什么 RPC(Remote Procedure Call)&#xff1a;即远程过程调用&#xff0c;目的是为了让计算机能够跟调用…

Android中实现滑动的7种方法

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

【C++进阶】面向对象

程序 编写程序是为了让计算机解决现实生活中的实际问题。pascal之父、结构化程序设计先驱Niklaus Wirth提出程序 算法 数据结构。程序是完成一定功能的一些列有序指令的集合。指令 操作码 指令。将指令按一定的顺序进行整合&#xff0c;就形成了程序。 机器语言与汇编语言…

软件测试的案例分析 - 闰年5

文章目的 显示不同的博客能获得多少博客质量分 &#xff08;这是关于博客质量分的测试 https://www.csdn.net/qc) 这个博客得了 83 分。怎么才能得到更多分数&#xff1f; 正文 我们谈了不少测试的名词, 软件是人写的, 测试计划和测试用例也是人写的, 人总会犯错误。错误发生…

Shiro学习认证和授权

Shiro学习笔记 认证 思路 获取当前的Subject&#xff0c;调用SecurityUtils.getSubject()方法&#xff1b;判断当前用户是否被认证&#xff0c;即是否已经登陆&#xff0c;调用Subject的isAuthenticated()方法进行判断&#xff1b;若没有认证&#xff0c;则把用户名和密码封…

C++11线程、互斥量以及条件变量

文章目录前言1、创建第一个线程2、线程对象的生命周期、等待和分离3、线程创建的多种方式4、互斥量4.1 独占的互斥量std::mutex4.2 递归独占互斥量recursive_mutex4.3 带超时的互斥量std::timed_mutex和std::recursive_timed_mutex4.4 std::lock_guard和std::unique_lock5、cal…

CSS常用内容总结(扫盲)

文章目录前言相关概念【了解】脚本语言什么是脚本语言脚本语言有什么特点常见的脚本语言什么是动态语言&#xff0c;什么是静态语言动态语言和静态语言两者之间有何区别CSSCSS是什么CSS的特点一、CSS代码怎么写基本语法规则引入方式内部样式内联样式表外部样式代码风格二、CSS的…

JavaWeb——进程详解

目录 一、操作系统 1、定义&#xff1a; 2、操作系统的基本功能&#xff1a; 二、进程 1、定义&#xff1a; 三、进程管理 1、PCB定义 &#xff08;1&#xff09;、身份标识 &#xff08;2&#xff09;、内存指针 &#xff08;3&#xff09;、文件描述符 2、操作系统…

Hadoop入门常见面试题与集群时间同步操作

目录 一&#xff0c;常用端口号 Hadoop3.x &#xff1a; Hadoop2.x&#xff1a; 二&#xff0c;常用配置文件&#xff1a; Hadoop3.x: Hadoop2.x: 集群时间同步&#xff1a; 时间服务器配置&#xff08;必须root用户&#xff09;&#xff1a; &#xff08;1&#xff09…

1639_perror的函数功能以及简单测试

全部学习汇总&#xff1a; GreyZhang/g_unix: some basic learning about unix operating system. (github.com) 继续分析之前的shell程序代码&#xff0c;看到了一个fork1的实现。 Fork之前还是看过的&#xff0c;但是也已经忘得差不多了&#xff0c;这个fork1就是fork的一种应…

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

本文目录如下&#xff1a;一、Nacos微服务治理Nacos 下载 与 启动Spring Cloud 集成 NacosIDEA 同一个 Application 启动多次一、Nacos微服务治理 Nacos 下载 与 启动 https://github.com/alibaba/nacos/releases Nacos 下载与启动: F:\ProgramFiles\nacos\bin> .\startup…

MyBatis操作数据库

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

stack,queue

stack,queuestack的介绍和使用介绍使用模拟实现queue的介绍和使用介绍使用模拟实现priority_queue的介绍和使用介绍使用模拟实现容器适配器概念标准库中stack&#xff0c;queue的底层结构介绍deque原理缺陷deque作为stack,queue底层默认容器stack的介绍和使用 介绍 stack是适…

哪个牌子的蓝牙耳机音质好?音质比较好的蓝牙耳机排名

蓝牙耳机经过多年发展&#xff0c;无论是在外观设计还是性能配置上都有很大的进步&#xff0c;越来越多的蓝牙耳机开始注重音质表现&#xff0c;逐渐有HIFI音质、无损音质出现在大众视野。那么哪个牌子的蓝牙耳机音质好&#xff1f;接下来&#xff0c;我来给大家分享几款音质比…

【H2实践】之 SpringBoot 与 H2 数据交互

一、目标 本文是【H2实践】之认识 H2&#xff0c;【H2实践】之 SpringBoot 整合的后续。前文分别介绍了 H2 及其简单使用&#xff0c;并完成了 H2 与 SpringBoot 的整合。本文将紧接 【H2实践】之 SpringBoot 整合 探索实用 SpringBoot 结合 JPA 通过 web 接口操作 H2 数据库的…

Python数据分析案例22——财经新闻可信度分析(线性回归,主成分回归,随机森林回归)

本次案例还是适合人文社科领域&#xff0c;金融或者新闻专业。本科生做线性回归和主成分回归就够了&#xff0c;研究生还可以加随机森林回归&#xff0c;其方法足够人文社科领域的硕士毕业论文了。 案例背景 有八个自变量&#xff0c;[微博平台可信度,专业性,可信赖性,转发量,…