使用QXmlStreamReader和QXmlStreamWriter读写XMl文件

来源:互联网 发布:软文写作软件 编辑:程序博客网 时间:2024/06/01 22:14

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对象。

说完了QXmlStreamReader,我们再来看看QXmlStreamWriter类。该类是和QXmlStreamReader配合使用的一个类,以流的方式向设备上写出xml文档。它提供了一系列的方便的API供我们使用,如writeStartDocument(),writeEndDocument()用来开始和结束一个xml文档;writeStartElement()和writeEndElement()用来开始和结束一个标记;writeAttribute()或writeAttributes()用来为标记添加属性;writeCharacters()用来添加标记间的具体内容。

另外,QXmlStreamWriter会自动地通过在元素间插入换行符和缩进空格来格式化xml数据,以使xml文档更便于人类阅读,也有助于某些源代码管理工具的显示。

默认情况下,QXmlStreamWriter是以utf-8来编码xml文档的,我们也可以使用setCodec()来设置不同的编码。

类似于QXmlStreamReader,如果在写入的过程中出现了错误,hasError()会返回真,并且后续的写入会被忽略。

下面,我们通过一个实例,来演示一下这两个的类的使用方法。

在这个例子中,我们使用QXmlStreamReader还读取一个xml文件,再使用QXmlStreamWriter将它写到控制台上。

其中,xml文件内容如下:

<?xml version="1.0" encoding="UTF-8"?><xbel version="1.0">    <folder folded="yes">        <title>Literate Programming</title>        <bookmark href="http://www.vivtek.com/litprog.html">            <title>Synopsis of Literate Programming</title>        </bookmark>    </folder></xbel>


先来看程序代码:

#include <QCoreApplication>#include <QXmlStreamReader>#include <QXmlStreamWriter>#include <QFile>#include <QDebug>int main(int argc, char *argv[]){    QCoreApplication a(argc, argv);    //设置输入文件    QFile inputfile("test.xml");    if(!inputfile.open(QIODevice::ReadOnly))    {        qDebug() << "Open input file failed";        return 0;    }    QXmlStreamReader reader(&inputfile);    //设置输出文件    QFile outputfile;    if(!outputfile.open(stdout, QIODevice::WriteOnly))    {        qDebug() << "Open output file failed";        return 0;    }    QXmlStreamWriter writer(&outputfile);    writer.setAutoFormatting(true);    //开始解析    while (!reader.atEnd())    {        QXmlStreamReader::TokenType token = reader.readNext();        switch (token)        {        case QXmlStreamReader::StartDocument:            writer.writeStartDocument();            break;        case QXmlStreamReader::EndDocument:            writer.writeEndDocument();            break;        case QXmlStreamReader::StartElement:            if(reader.name() == "xbel")            {                writer.writeStartElement(reader.name().toString());                writer.writeAttribute("version", reader.attributes().value("version").toString());            }            else if(reader.name() == "folder")            {                writer.writeStartElement(reader.name().toString());                writer.writeAttribute("folded", reader.attributes().value("folded").toString());            }            else if(reader.name() == "title")            {                writer.writeStartElement(reader.name().toString());            }            else if(reader.name() == "bookmark")            {                writer.writeStartElement(reader.name().toString());                writer.writeAttribute("href", reader.attributes().value("href").toString());            }            break;        case QXmlStreamReader::EndElement:            writer.writeEndElement();            break;        case QXmlStreamReader::Characters:            writer.writeCharacters(reader.text().toString());            break;        default:            break;        }    }    //是否是正常结束    if (reader.error())    {        qDebug() << "Error: " << reader.errorString() << "in file test.xml at line " << reader.lineNumber() << ", column " << reader.columnNumber();        return 0;    }    //关闭文件    inputfile.close();    outputfile.flush();    outputfile.close();    return a.exec();}
程序逻辑也非常的简单。我们先使用QFile打开要解析的xml文件,然后使用该文件初始化一个QXmlStreamReader;再打开我们的输出文件,在这里就是控制台,即stdout,然后使用其来初始化一个QXmlStreamWriter对象,并设置该对象的自动格式化;最后,使用一个while循环,解析该xml文件直到结束或出错。而具体的解析过程就是,先使用readNext()来读取一个标记,然后判断该标记的具体类型,根据不同的类型做不同的处理,此处即为向控制台写出同样的内容。

运行结果如下:

这两个类的基本使用方法就是这样。当然,至于xml文档中,其他标记的处理,比如实体,实体引用,CDATA,命名空间,这两个类中也都提供了相应的API,大家可以在需要时自行参考帮助文件即可。

当然,Qt中对xml的支持,处了这两个类之外,还有其他的xml操作方式,我们会在以后再来学习其他的xml解析方法。

0 0
原创粉丝点击