当前位置:   article > 正文

Qt项目(5)Qt中使用XML格式文档_qt xml

qt xml

在这里插入图片描述

1.介绍XML

XML(eXtensible Markup Language)是一种通用的文本格式,与数据库一样被广泛运用于数据交换和数据存储,但在使用场景上二者有着巨大的差别,这是由于自身属性决定的。与数据库相比,XML缺少数据库具备的特性:高效的存储、索引和数据修改机制;严格的数据安全访问控制;完整的事务和数据一致性控制;多用户访问机制;触发器、完善的并发控制等。因此,用户量大、数据集成度高以及性能要求高的数据环境中还是需要数据库来完成任务,可以进行大量数据的存储和分析。

而xml是一种存储数据的标准格式,是为了便于网络数据传输和交互,解决的是数据在网上传输标准的问题,把原来各种各样的数据孤岛可以通过xml这座桥梁连接起来,而存在的期性能和存储的数据量,严重受着文件的大小而影响,存储xml的文件越大传递和读取起来越耗时。所以打个比方,数据库就好比是盛数据的桶,而xml则是数据传输转换的桥梁,所以数据库是数据库,xml是xml,二者是截然不同的,当然二者也存在非常紧密的联系,毕竟都是处理数据的工具。

2.Qt对于XML的支持

Qt 提供提供了Qt XML模块来进行XML文档的处理,主要提供了DOM方法和SAX方法。

DOM(Document Object Model)即文档对象模型,提供了一个接口来访问和改变一个XML文件的内容、结构,可以将XML文档表示为一个存储在内存中具有层次的树形视图,使用DOM可以很方便地进行XML的随机访问与增删改查,这是他最大的优点。与此同时带来的问题是需要一次性将整个XML文档读入内存,会占用很大的内存。
SAX(Simple API for XML)为XML解析器提供了一个基于事件的标准接口。如果不需要对文档进行操作,只需要读取整个XML文档,那么使用SAX方法最高效。不过他只能读取XML文档。
此外,从Qt4.3开始引入了两个新的类来读取和写入XML文档:QXmlStreamReader和QXmlStreamWriter,一种快速的基于流的方式访问良格式 XML 文档。它是作为SAX解析器的替代品身份出现的,它比SAX解析器更快更方便。本文介绍DOM方法与XML流方法。

3.DOM方法读写XML

3.1XML术语

<?xml version="1.0" encoding="UTF-8"?>
<Begin>
	<Type1 myLover="yxx">	
		<sex>girl</sex>
		<school>XiAn</school>
		<age>1996</age>
		<special>beautiful</special>
		<relationship>classmate</relationship>
		<duration>2years</duration>
	</Type1>
	<Type2 myLover="83">	
		<aa>woceng</aa>
		<bb>aiguo</bb>
		<cc>yeshiqu</cc>
		<dd>guo</dd>
		<ee>changguo</ee>
		<ff>aide</ff>
	</Type2>
	<Type3>	
		<yy>tianyuse</yy>
		<xx>521</xx>
		<xx>0803</xx>
		<ll>!!!</ll>
		<oo>give</oo>
		<vv>me</vv>
		<ee>hug</ee>
	</Type3>
	
</Begin>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

一个XML文件如上所示,首先应该搞清楚几个常用的术语。以下代码中,
第一行说明该文件的版本号和编码;"begin"是根节点,可以理解为一级目录;
”TypeX“是子节点,可以理解为二级目录;”myLover“是属性,”yxx“是属性值,属性值必须在双引号之内,属性写在尖括号内,多个属性时,也要全部写在尖括号内;“sex”“school”等是三级节点,可以理解为三级目录;”girl“”“Xian”等是文本;在使用DOM读取之前,先看一下在XML模块中的Qt方法与该xml文件之间的对应关系。
在这里插入图片描述
下面在贴上使用DOM读的效果图,上述xml中的信息都可以被获取,可与上幅图片对比看。
在这里插入图片描述

3.2Dom方法读XML源代码

void Widget::domRead()
{
QString fileName = QFileDialog::getOpenFileName(this,"get xml file",
                                                    "C:/Users/Jack_Chen/Desktop","xml(*.xml)");
    if(fileName.isEmpty()){
        return;
    }
    myFile->setFileName(fileName);
    bool ret =myFile->open(QIODevice::ReadOnly|QIODevice::Text);
    if(!ret){
        QMessageBox::warning(this,"warning","打开文件失败!");
        return;
    }
    //对xml文件操作
    QDomDocument doc("yxx");//定义doc对象,初始化名字
    bool isLot = doc.setContent(myFile);//将文件与QDomDocument类关联
    if(!isLot){
        //关联xml文件失败
        myFile->close();
        QMessageBox::warning(this,"waring","关联文件失败");
        return;
    }
    myFile->close();
    QDomElement firstElem = doc.documentElement();//获取到了<begin>一级目录
    qDebug()<<"一级目录<begin>: "<<firstElem.nodeName();
    QDomNodeList secondList = firstElem.childNodes();//获取全部二级目录<Type1><Type2><Typ3>
    for (int i = 0; i < secondList.count(); ++i) {
        QString str = QString("      第 %1 个二级目录节点:").arg(i+1);
        qDebug()<<str.toUtf8().data()<<secondList.at(i).nodeName();//将QString转换成char *,使用qDebug输出没有引号
        QDomElement secondElem = secondList.at(i).toElement();//获取二级目录节点的属性值管理者domElement
        if(secondElem.hasAttribute("myLover")){
            //输出二级目录节点的属性值
            qDebug()<<"      "<<secondElem.nodeName().append("的属性值myLover为").toUtf8().data()<<secondElem.attribute("myLover");
        }
        else{
            qDebug()<<"      "<<secondElem.nodeName().append("该节点无属性值").toUtf8().data();
        }
        QDomNodeList thirdList = secondList.at(i).childNodes();//获取三级目录<具体值></具体值>
        for(int j = 0;j<thirdList.length();j++ ){
            QDomElement thirdElement=thirdList.at(j).toElement();
            QString str_1 = QString("            %1中第 %2 个三级目录节点 %3,他的文本值为 %4:")
                    .arg(secondList.at(i).nodeName(),QString::number(j+1),thirdList.at(j).nodeName(),thirdElement.text());
            qDebug()<<str_1.toUtf8().data();//将QString转换成char *,使用qDebug输出没有引号
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

3.3Dom方法写XML

写XML文档应该按照XML自身的格式来写,首先写第一行关于版本和编码的描述,然后加入各级目录节点的同时,加入与节点对应的属性值和文本。最后使用QDomDocument类提供的save方法,将QTextStream类与文件关联进行保存。源码部分与效果图粘贴如下。

void Widget::domWrite()
{
    //add instruction
    QDomDocument doc;
    QDomProcessingInstruction instruction = doc.createProcessingInstruction("xml","version=\"1.0\" encoding=\"UTF-8\"");//写xml中的第一行说明
    doc.appendChild(instruction);

    //add 第一级目录节点
    QDomElement firstNode = doc.createElement("Begin");
    doc.appendChild(firstNode);

    //add 第二级目录节点
    QDomElement secondNode = doc.createElement("Type");
    //为二级目录添加属性值
    QDomAttr secondAttr = doc.createAttribute("MyLover");
    secondAttr.setValue("yxx");
    secondNode.setAttributeNode(secondAttr);
    firstNode.appendChild(secondNode);//一级目录下添加二级节点
        //add 第三级目录节点 添加text文本值
        QDomElement thirdNode_1 = doc.createElement("sex");
        secondNode.appendChild(thirdNode_1);
        QDomText t1 = doc.createTextNode("girl");
        thirdNode_1.appendChild(t1);
        QDomElement thirdNode_2 = doc.createElement("school");
        secondNode.appendChild(thirdNode_2);
        QDomText t2 = doc.createTextNode("Xian");
        thirdNode_2.appendChild(t2);
        QDomElement thirdNode_3 = doc.createElement("age");
        secondNode.appendChild(thirdNode_3);
        QDomText t3 = doc.createTextNode("1996");
        thirdNode_3.appendChild(t3);
        QDomElement thirdNode_4 = doc.createElement("special");
        secondNode.appendChild(thirdNode_4);
        QDomText t4 = doc.createTextNode("beautiful");
        thirdNode_4.appendChild(t4);
    QString fileName = QFileDialog::getSaveFileName(this,"Save xml file",
                                                        "C:/Users/Jack_Chen/Desktop","xml(*.xml)");
    if(fileName.isEmpty()){
        return;
    }
    myFile->setFileName(fileName);
    bool ret =myFile->open(QIODevice::WriteOnly|QIODevice::Text);
    if(!ret){
        QMessageBox::warning(this,"warning","保存文件失败!");
        return;
    }
    QTextStream stream(myFile);//使用QTextStream类与文件关联进行保存xml!
    stream.setCodec("UTF-8");//编码格式
    doc.save(stream,4);//4代表的是缩进个数
    myFile->close();
    QMessageBox::information(this,"save tip","xml文件保存成功!");
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52

保存xml文件如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<Begin>
    <Type MyLover="yxx">
        <sex>girl</sex>
        <school>Xian</school>
        <age>1996</age>
        <special>beautiful</special>
    </Type>
</Begin>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

3.4 DOM源码分析

  • 1.在3.1映射图中可以清楚的看到,除了一级目录外的次级目录(包括二级、三级以及之后的)的节点都保存在QDomNode类中,但是节点的属性、属性值、文本都只有在QDomElement类中才提供读取的方法。因此,读取节点的属性(值)以及文本需要将QDomNode类转换成QDomElement,通常的做法是,使用childNodes方法,获取次级目录中节点的个数,将其保存在QDomNodeList中,遍历这个列表,将每一个索引值都转换成QDomElement进而获取属性(值)、文本。
  • 2.使用QDomDocument类之前先在pro文件中写入 QT += xml
  • 3.str.toUtf8().data(),这个操作将会有QString类型转换成char *类型,这样做的好处是,使用qDebug打印数据到控制台时,将不带双引号。
  • 4.DOM读的源码是,将读功能封装成了一个函数,函数执行时,会弹出文件选择框,过滤除xml格式的其他文件,用户打开xml将在控制台打印各级目录信息,属性信息以及文本信息。上述效果截图中正是打开的上述的xml文件

4.流方法读写XML

4.1QXmlStreamReader方法读XML

关联QXmlStreamReader类与文件类,然后用QXmlStreamReader类来操作,但切记,未操作完不能close文件类,否则无法正确读取。该类根据标记读取,读取到不同的标记会返回不同的值,与DOM思想完全不一样,但是不支持随机读取。标记的划分是根据XML语法中的属性(值)文本,开始结束关键字进行的。贴出代码与运行效果。
代码:

void Widget::streamRead()
{
    QString fileName = QFileDialog::getOpenFileName(this,"get xml file",
                                                    "C:/Users/Jack_Chen/Desktop","xml(*.xml)");
    if(fileName.isEmpty()){
        return;
    }
    myFile->setFileName(fileName);
    bool ret =myFile->open(QIODevice::ReadOnly|QIODevice::Text);
    if(!ret){
        QMessageBox::warning(this,"warning","打开文件失败!");
        return;
    }
    QXmlStreamReader readxml(myFile);
    while(!readxml.atEnd()){//直到读完停止读
        if(readxml.hasError()){//如果有错误
           QMessageBox::warning(this,"warning","读取文件失败!");
           break;
        }
          QXmlStreamReader::TokenType type = readxml.readNext();//指定读取标记类型
           switch ((int)type) {
           case QXmlStreamReader::ProcessingInstruction:
               qDebug()<<"ProcessingInstruction"<<readxml.text();
               break;
           case QXmlStreamReader::StartDocument://第一句话
               qDebug()<<"StartDocument"<<"输出版本编码信息"<<readxml.documentVersion()<<readxml.documentEncoding();
               break;
           case QXmlStreamReader::StartElement://属性值
           {
               qDebug()<<readxml.name().toString().append("!!!");
               QString str = readxml.attributes().value("myLover").toString();
               if(!str.isEmpty()){
                   qDebug()<<"输出属性值"<<readxml.name()<<str;
               }
               break;
           }
           case QXmlStreamReader::EndElement://endof element
//               qDebug()<<"EndElement";
               break;
           case QXmlStreamReader::EndDocument://末尾
               qDebug()<<"EndDocument";
               break;
           case QXmlStreamReader::Characters://text文本值
           {
               QString ss = readxml.text().toString();
               if(!readxml.isWhitespace()){//如果读到的字符全部是空格返回true
                    qDebug()<<"输出文本值"<<ss;
               }
               break;
           }
           }
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

打开的xml文件为本贴前文最上面部分的xml文件,效果如下:
在这里插入图片描述

4.2 QXmlStreamWriter方法写XML

流方法的思想除了数据还有标记的概念,通过标记区分位置,因此除了写数据之外还要写一些标记。跟读的思想完全一样,只不过具体的函数不太一样。直接贴代码

void Widget::streamWrite()
{
    QString fileName = QFileDialog::getSaveFileName(this,"Save xml file",
                                                        "C:/Users/Jack_Chen/Desktop","xml(*.xml)");
    if(fileName.isEmpty()){
        return;
    }
    myFile->setFileName(fileName);
    bool ret =myFile->open(QIODevice::WriteOnly|QIODevice::Text);
    if(!ret){
        QMessageBox::warning(this,"warning","保存文件失败!");
        return;
    }
   QXmlStreamWriter stream(myFile);
   stream.setAutoFormatting(true);//设置自动对齐
   stream.writeStartDocument();//开始印记
   //add 一级目录
     stream.writeStartElement("begin");
        //add 二级目录节点
        stream.writeStartElement("Type");
        //添加属性值 myLover
        stream.writeAttribute("myLover","yxx");
        //add 三级目录节点
            stream.writeStartElement("sex");
            //add text
            stream.writeCharacters("girl");
            stream.writeEndElement();

            stream.writeStartElement("school");
            //add text
            stream.writeCharacters("Xian");
            stream.writeEndElement();

            stream.writeStartElement("age");
            //add text
            stream.writeCharacters("1996");
            stream.writeEndElement();

            stream.writeStartElement("special");
            //add text
            stream.writeCharacters("beautiful");
            stream.writeEndElement();

        stream.writeEndElement();
     stream.writeEndElement();
   stream.writeEndDocument();//结束印记
    myFile->close();
    QMessageBox::information(this,"save tip","xml文件保存成功!");
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

写完打开文件得到xml如下:

<?xml version="1.0" encoding="UTF-8"?>
<begin>
    <Type myLover="yxx">
        <sex>girl</sex>
        <school>Xian</school>
        <age>1996</age>
        <special>beautiful</special>
    </Type>
</begin>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

5.传送门

推荐阅读
相关标签