Qt解析XML文件的两种方式

1 XML简介

XML是ExtensibleMarkup Language的缩写,即可扩展标记语言。它是一种用来创建的标记的标记语言。使用XML标记语言可以做到数据或数据结构在任何编程语言环境下的共享。XML中有格式正规的XML和有效的XML。
XML文档的元素一般是由标记头、标记末和标记间的字符串数据构成。元素可包含其他元素、文本或者两者的混合物。元素也可以拥有属性。一般格式: Aquotion。元素中可以插入属性,但是属性值一定要用双引号。
XML解析器是读取XML文档并提供对文档内容的访问的软件模块。在应用程序中,和其他系统中使用xml后,可以轻松实现数据交换。

1.Qt读取文件的方式

A、QXmlStreamReader:一种快速的基于流的方式访问良格式 XML 文档,特别适合于实现一次解析器(所谓“一次解析器”,可以理解成我们只需读取文档一次,然后像一个遍历器从头到尾一次性处理 XML 文档,期间不会有反复的情况,也就是不会读完第一个标签,然后读第二个,读完第二个又返回去读第一个,这是不允许的);这种方法是通过调用readNext()函数实现的,可以读取下一个记号,然后返回一个记号类型。这是一种快速解析

B、DOM(Document Object Model):将整个 XML 文档读入内存,构建成一个树结构,允许程序在树结构上向前向后移动导航,这是与另外两种方式最大的区别,也就是允许实现多次解析器(对应于前面所说的一次解析器)。DOM 方式带来的问题是需要一次性将整个 XML 文档读入内存,因此会占用很大内存;但是因为储存在内存里,所以实现频繁修改比较方便

2.Qt生成XML文件的方法

A、QXmlStreamWriterQXmlStreamReader相对应。XML流的方法
B、DOM 方式,首先在内存中生成 DOM 树,然后将 DOM 树写入文件。

2 Qt中DOM的使用

项目配置
pro文件里面添加QT+=xml
include <QtXml>,也可以include <QDomDocument>

1.写操作

void writeXml()
{
    QString strDir = QCoreApplication::applicationDirPath() + "/xmlfile";
    QDir dir(strDir);


    if(!dir.exists(strDir))//先创建个文件夹
    {
        dir.cd(strDir);
        dir.mkdir(strDir);
    }

    dir.setCurrent(strDir);

    m_filePath = strDir + "/text.xml";

    QFile file(m_filePath); //相对路径、绝对路径、资源路径都可以
    if(!file.open(QFile::WriteOnly)) //可以用QIODevice,Truncate表示清空原来的内容
        return;

    QDomDocument doc;
    QDomProcessingInstruction instruction; //添加处理命令
    instruction = doc.createProcessingInstruction("xml","version=\"1.0\" encoding=\"UTF-8\"");
    doc.appendChild(instruction);
    //添加根节点
    QDomElement root = doc.createElement("library");
    doc.appendChild(root);
    //添加第一个子节点及其子元素
    QDomElement book = doc.createElement("book");
    book.setAttribute("id",1); //方式一:创建属性  其中键值对的值可以是各种类型
    QDomAttr time = doc.createAttribute("time"); //方式二:创建属性 值必须是字符串
    time.setValue("2013/6/13");
    book.setAttributeNode(time);
    QDomElement title = doc.createElement("title"); //创建子元素
    QDomText text; //设置括号标签中间的值
    text = doc.createTextNode("C++ primer");
    book.appendChild(title);
    title.appendChild(text);
    QDomElement author = doc.createElement("author"); //创建子元素
    text = doc.createTextNode("Stanley Lippman");
    author.appendChild(text);
    book.appendChild(author);
    root.appendChild(book);

    //添加第二个子节点及其子元素,部分变量只需重新赋值
    book = doc.createElement("book");
    book.setAttribute("id",2);
    time = doc.createAttribute("time");
    time.setValue("2007/5/25");
    book.setAttributeNode(time);
    title = doc.createElement("title");
    text = doc.createTextNode("Thinking in Java");
    book.appendChild(title);
    title.appendChild(text);
    author = doc.createElement("author");
    text = doc.createTextNode("Bruce Eckel");
    author.appendChild(text);
    book.appendChild(author);
    root.appendChild(book);

    //输出到文件
    QTextStream out_stream(&file);
    doc.save(out_stream,4); //缩进4格
    file.close();
}

2.读操作

void readXml()
{
    //打开或创建文件
    QFile file(m_filePath); //相对路径、绝对路径、资源路径都行
    if(!file.open(QFile::ReadOnly))
        return;

    QDomDocument doc;
    if(!doc.setContent(&file))
    {
        file.close();
        return;
    }
    file.close();

    QDomElement root = doc.documentElement(); //返回根节点
    qDebug() << root.nodeName();
    QDomNode node = root.firstChild(); //获得第一个子节点
    while(!node.isNull())  //如果节点不空
    {
        if(node.isElement()) //如果节点是元素
        {
            QDomElement e = node.toElement(); //转换为元素,注意元素和节点是两个数据结构,其实差不多
            qDebug() << e.tagName() << " " << e.attribute("id") << " " << e.attribute("time"); //打印键值对,tagName和nodeName是一个东西
            QDomNodeList list= e.childNodes();
            for(int i = 0;i < list.count(); i++) //遍历子元素,count和size都可以用,可用于标签数计数
            {
                QDomNode n = list.at(i);
                if(node.isElement())
                    qDebug()<<n.nodeName()<<":"<<n.toElement().text();
            }
        }
        node = node.nextSibling(); //下一个兄弟节点,nextSiblingElement()是下一个兄弟元素,都差不多
    }
}

3.添加

void addXml()
{
    //打开文件
    QFile file(m_filePath); //相对路径、绝对路径、资源路径都可以
    if(!file.open(QFile::ReadOnly))
        return;

    //增加一个一级子节点以及元素
    QDomDocument doc;
    if(!doc.setContent(&file))
    {
        file.close();
        return;
    }
    file.close();

    QDomElement root = doc.documentElement();
    QDomElement book = doc.createElement("book");
    book.setAttribute("id",3);
    book.setAttribute("time","1813/1/27");
    QDomElement title = doc.createElement("title");
    QDomText text;
    text = doc.createTextNode("Pride and Prejudice");
    title.appendChild(text);
    book.appendChild(title);
    QDomElement author = doc.createElement("author");
    text = doc.createTextNode("Jane Austen");
    author.appendChild(text);
    book.appendChild(author);
    root.appendChild(book);

    if(!file.open(QFile::WriteOnly)) //先读进来,再重写,如果不用truncate就是在后面追加内容,就无效了
        return;
    //输出到文件
    QTextStream out_stream(&file);
    doc.save(out_stream,4); //缩进4格
    file.close();
}

4.删除

void removeXml()
{
    //打开文件
    QFile file(m_filePath); //相对路径、绝对路径、资源路径都可以
    if(!file.open(QFile::ReadOnly))
        return;

    //删除一个一级子节点及其元素,外层节点删除内层节点于此相同
    QDomDocument doc;
    if(!doc.setContent(&file))
    {
        file.close();
        return;
    }
    file.close();  //一定要记得关掉啊,不然无法完成操作

    QDomElement root = doc.documentElement();
    QDomNodeList list = doc.elementsByTagName("book"); //由标签名定位
    for(int i = 0;i < list.count(); i++)
    {
        QDomElement e = list.at(i).toElement();
        if(e.attribute("time")=="2007/5/25")  //以属性名定位,类似于hash的方式,warning:这里仅仅删除一个节点,其实可以加个break
            root.removeChild(list.at(i));
    }

    if(!file.open(QFile::WriteOnly|QFile::Truncate))
        return;
    //输出到文件
    QTextStream out_stream(&file);
    doc.save(out_stream,4); //缩进4格
    file.close();
}

5.更新

void updateXml()
{
    //打开文件
    QFile file(m_filePath); //相对路径、绝对路径、资源路径都可以
    if(!file.open(QFile::ReadOnly))
        return;

    //更新一个标签项,如果知道xml的结构,直接定位到那个标签上定点更新
    //或者用遍历的方法去匹配tagname或者attribut,value来更新
    QDomDocument doc;
    if(!doc.setContent(&file))
    {
        file.close();
        return;
    }
    file.close();

    QDomElement root = doc.documentElement();
    QDomNodeList list = root.elementsByTagName("book");
    QDomNode node = list.at(list.size()-1).firstChild(); //定位到第三个一级子节点的子元素
    QDomNode oldnode = node.firstChild(); //标签之间的内容作为节点的子节点出现,当前是Pride and Projudice
    node.firstChild().setNodeValue("Emma");
    QDomNode newnode = node.firstChild();
    node.replaceChild(newnode,oldnode);

    if(!file.open(QFile::WriteOnly|QFile::Truncate))
        return;
    //输出到文件
    QTextStream out_stream(&file);
    doc.save(out_stream,4); //缩进4格
    file.close();
}

3 Qt中的QXmlStreamWriter和QXmlStreamReader

1.QXmlStreamWriter

对应常用api介绍:
 writeStartDocument():写文档头,作用类似于创建一个xml文档,并在文档开头部分写入版本信息和编码信息默认为:
  <?xml version="1.0" encoding="UTF-8"?>
同时也可以写1个参数与2个参数.
writeEndDocument(): 对应于writeStartDocument(),当调用这个函数时,即表示文档信息写入完毕。
writeStartElement():  写入开始记号 比如<SMS>
writeEndElement():  写入结束记号 比如</SMS>
writeTextElement():  写入文本信息 比如上面的name,phone,time 等等;
void xmlStreamWriter()
{
    QString strDir = QCoreApplication::applicationDirPath() + "/xmlfile";
    QDir dir(strDir);


    if(!dir.exists(strDir))//先创建个文件夹
    {
        dir.cd(strDir);
        dir.mkdir(strDir);
    }

    dir.setCurrent(strDir);

    m_xmlPath = strDir + "/xmltest.xml";

    QStringList str;
    str << "通天" << "帝江" << "祖龙" << "红云";

    QFile file(m_xmlPath);
    if(!file.open(QFile::WriteOnly|QFile::Text))
    {
        qDebug()<< "file cannot open";
        return;
    }
    QXmlStreamWriter stream(&file);
    stream.setAutoFormatting(true); //
    stream.writeStartDocument(); //默认: <?xml version="1.0" encoding="UTF-8"?>
    stream.writeStartElement("DATA");
    for (int i = 0; i < str.size(); i++)
    {
        stream.writeStartElement("EP");//
        stream.writeTextElement("name", str[i]);
        stream.writeTextElement("phone", "188XXXXXXXXXX");
        stream.writeTextElement("time", "2022");
        stream.writeEndElement();
    }

    stream.writeEndElement();
    stream.writeEndDocument();
    file.close();
}

文件内容:

<?xml version="1.0" encoding="UTF-8"?>
<DATA>
    <EP>
        <name>通天</name>
        <phone>188XXXXXXXXXX</phone>
        <time>2022</time>
    </EP>
    <EP>
        <name>帝江</name>
        <phone>188XXXXXXXXXX</phone>
        <time>2022</time>
    </EP>
    <EP>
        <name>祖龙</name>
        <phone>188XXXXXXXXXX</phone>
        <time>2022</time>
    </EP>
    <EP>
        <name>红云</name>
        <phone>188XXXXXXXXXX</phone>
        <time>2022</time>
    </EP>
</DATA>

2.QXmlStreamReader

常用的api:

 readNext(): 从xml输入流中读取下一个记号。

 name(): 记号的名称,即<名称></名称>

 isStartElement():判断当前已读取的记号是否为开始元素,开始元素即<>

 isEndElement():判断当前已读取的记号是否为结束元素,结束元素即</>

 readElementText():读取当前记号对应的文本值,<>文本值</>

 atEnd():判断是否为文件结尾
void xmlStreamReader()
{
    QString str;
    QFile file(m_xmlPath);
    if(file.open(QFile::ReadOnly|QFile::Text))
    {
        QXmlStreamReader reader(&file);
        reader.readNext(); //过滤掉头一行
        while(!reader.atEnd())
        {
            if(reader.isStartElement())
            {
                if(reader.name() == "EP")
                {
                    str = "EP";
                }
                else if (reader.name() == "name")
                {
                    str = reader.readElementText();
                    qDebug() << str << endl;
                }
                else if (reader.name() == "phone")
                {
                    str = reader.readElementText();
                    qDebug() << str << endl;
                }
                else if(reader.name() == "time" )
                {
                    str = reader.readElementText();
                    qDebug() << str << endl;
                }

            }
            else if (reader.isEndElement())
            {
                if(reader.name() == "EP")
                {
                    qDebug()<<endl;
                }
            }
            reader.readNext();

        }


    }
    else {
        qDebug() << "not file is here or it's a broken";
    }
    file.close();
}

输出结果:

"通天" 
"188XXXXXXXXXX" 
"2022" 

"帝江" 
"188XXXXXXXXXX" 
"2022" 

"祖龙" 
"188XXXXXXXXXX" 
"2022" 
 
"红云" 
"188XXXXXXXXXX" 
"2022" 
  • 3
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值