baizy77 发表于 2018-11-19 11:45:35

【独家连载】Qt入门与提高:KS04-13 配置文件-XML格式

本帖最后由 baizy77 于 2019-7-2 20:35 编辑

版权声明---------------------------------------------------------------------------------------------------------------------该文章原创于Qter开源社区(www.qter.org)作者: 女儿叫老白转载请注明出处!---------------------------------------------------------------------------------------------------------------------课程目录: 【独家连载】《Qt入门与提高-GUI产品开发》目录
网页版课程源码 提取码:1uy7
引言----------------------------------------------------------------------------------------------------------------------在进行项目或者产品研发时,我们的软件模块为了适应不同的应用场景,经常需要进行灵活配置,而配置文件可能会以如下形式存在:l unix风格的配置文件l xml格式l ini格式今天开始,我们为大家介绍配置文件的设计与访问。正文---------------------------------------------------------------------------------------------------------------------- unix风格的配置文件不是本套教程的重点,在此仅做一下简单介绍,我们看一个示例:#
# The networkconfiguration file. This file is currently only used in
# conjunction with the TI-RPCcode in the libtirpc library.
#
# Entries consist of:
#
#       <network_id> <semantics><flags> <protofamily> <protoname> \
#               <device><nametoaddr_libs>

# The <device> and<nametoaddr_libs> fields are always empty in this
# implementation.
#
udp      tpi_clts      v      inet   udp   -      -
tcp      tpi_cots_ordv      inet   tcp   -      -
udp6       tpi_clts      v      inet6    udp   -       -
tcp6       tpi_cots_ordv      inet6    tcp   -      -
rawip      tpi_raw       -      inet      -      -      -
local      tpi_cots_ord-      loopback-      -      -
unix       tpi_cots_ord-      loopback-      -      -    上面的示例是/etc/netconfig文件的内容。可以看出它既不是xml格式也不是ini格式的配置。这种配置比较灵活,因此不同的配置文件也没有互通性。也就是说每次访问某个这种风格的配置文件可能都需要重新编程。    下面进入本节主题,xml格式的配置文件。    XML(Extensible Markup Language),中文名称为:可扩展标记语言。1998年2月,W3C正式批准了可扩展标记语言的标准定义。可扩展标记语言可以对文档和数据进行结构化处理,以便在不同的应用之间交换数据。我们来看一个XML的例子:<?xml version="1.0" encoding="UTF-8"?>
    <bookshelf>
      <bookcategory="student">
            <title>How to Learn Qt5</title>
             <author>女儿叫老白</author>
            <year>2018</year>
            <price>?</price>
      </book>
    <bookcategory="teacher">
      <title>C++跨平台开发干货系列教程教学指南</title>
      <author>女儿叫老白</author>
      <year>2019</year>
      <price>?</price>
    </book>
</bookshelf>     我们看到,第一行是 XML 声明。它定义 XML 的版本(1.0)和所使用的编码(UTF-8 : 万国码, 可显示各种语言)。关于编码,大家可以看这篇文章[注1]。    从第二行开始就是XML的内容了,实际上,XML是一种带有层级的描述,可以嵌套。比如<bookshelf>包含了两个<book>元素。XML的元素可以带有属性和值,比如<book>元素的:   category="student"    一个元素可以带有多个属性和值的键值对,比如下面的XML文件片段:<circlecx="100" cy="50" r="40"stroke="black"   stroke-width="2" fill="red" />     OK,现在我们来为大家介绍XML文件的存取。    我们先看一下XML文件的保存,比如我们希望保存如下的XML文件:<?xml version="1.0"encoding="GB2312"?>
<doc>
    <coursesinstitution="星点课堂" teacher="女儿叫老白" count="7">
         <lesson id="1" fee="免费"url="https://study.163.com/instructor/1143174933.htm">C++老鸟日记</lesson>
         <lesson id="2" fee="免费"url="https://study.163.com/instructor/1143174933.htm">C++跨平台开发中的编译错误</lesson>
         <lesson id="3"url="https://study.163.com/instructor/1143174933.htm">Qt入门与提高-GUI产品开发</lesson>
         <lesson id="4" url="sorry, not ready">C++跨平台服务模块开发</lesson>
   </courses>
</doc>
    我们先来总体看一下这个XML文件。它的根为doc元素,doc下面有一个子元素courses,courses元素下面有4个lesson子元素。    我们分为XML文件创建和读取两个场景来介绍。场景1. 首先我们从创建XML文件开始:QStringstrPath = QCoreApplication::applicationDirPath();
strPath =ns_train::getPath(strPath);
if(!strPath.endsWith("/")) {
   strPath+= "/";
}

QStringstrFileName = strPath + "test04_13.xml"; // 程序运行目录下的xml文件
QFilefile(strFileName);
if(!file.open(QFile::WriteOnly | QFile::Text | QFile::Truncate))   { // QFile::Truncate,需要清空原来的内容
   return;
}     首先,为了方便我们选择将文件保存到运行程序所在目录。这里用到了QCoreApplication::applicationDirPath()。所以在此之前,我们需要在main()函数中构造一个QApplication对象。得到目录的路径之后,为了以防万一,我们为路径添加了最后的"/"。然后组织文件名。打开文件时用了:只写、文本、清空这3个选项。    然后,我们利用QTextStream来写文件,并设置了字符编码:QTextStreamout(&file);
out.setCodec("GB2312");    创建XML文档对象QDocument:QDomDocumentdocument;
QDomProcessingInstructioninstruction;//添加处理指令
// 请注意此处的编码必须和您的源代码文件的编码保持一致。
instruction= document.createProcessingInstruction("xml",
          "version=\"1.0\"encoding=\"GB2312\"");
    document.appendChild(instruction);    上述代码中的QDomProcessingInstruction用来写入XML的第一行:版本号和编码。我们这里设置为1.0版,编码为"GB2312"。    最后一句document.appendChild(instruction)用来将instruction添加到document对象。    然后,我们开始创建XML的第一个根元素"doc"并将它作为子节点添加到document对象:// doc
QDomElement rootDoc = document.createElement("doc");
document.appendChild(rootDoc);    请注意,创建元素接口为QDocument::createElement()。    然后,我们创建"courses"元素并将它作为子节点添加到rootDoc(也就是"doc"元素)。//courses
QDomElementeleCourses = document.createElement("courses");
rootDoc.appendChild(eleCourses);    然后,我们调用setAttribute()接口为元素添加属性:strName ="institution";
strValue =QString::fromLocal8Bit("星点课堂");
eleCourses.setAttribute(strName,strValue);    为了显示课程名称,我们使用QDomDocument::createTextNode()创建了QDomText对象,并将其作为子节点添加到eleLesson    用同样的方法,我们设置了“teacher”、“count”等属性。QDomTextdtText = document.createTextNode(QString::fromLocal8Bit("C++老鸟日记"));
eleLesson.appendChild(dtText);<span style="background-color: rgb(255, 255, 255);">    请注意,我们用到汉字时调用了QString::fromLocal8Bit()。</span>    等所有属性设置都设置完毕,我们再将eleLesson对象添加到其父节点。eleCourses.appendChild(eleLesson);    请注意,如果在设置eleLesson对象的属性之前将它添加到eleCourses,那么将添加一个没有属性的子节点给eleCourses,因为appendChild()接口传入的是拷贝而不是引用。    我们用同样的方法将其他几门课程添加到eleCourses。为了防止eleLesson有缓存,我们每次都重新生成新的节点。    最后,我们将XML内容写入文件并关闭。document.save(out,4, QDomNode::EncodingFromTextStream);   // 4:缩进值
file.close();     上述代码中,我们将XML的缩进设置为4。场景2. 读取XML文件:    读取XML时,我们使用了只读和文本两个设置。QFile file(strFileName);
if (!file.open(QFile::ReadOnly | QFile::Text)){
    return;
}通过QDomDocument::setContent()接口可以读取整个XML到内存:QDomDocumentdocument;
    QString error;
    int row = 0,column = 0;
    if (!document.setContent(&file,false, &error, &row, &column)){
    return;
    }第二个参数false表示不用解析命名空间,如果您的XML中需要使用命名空间的话,将该值设置为true。    error用来存放打开文件过程中产生的错误信息。row和column用来记录出错时的行列数。    然后,我们获取第一个根元素,并判断是否是我们所期望的节点元素:QDomElement rootDoc = document.firstChildElement();
if (rootDoc.nodeName() != "doc") {
    return;
}    然后,我们获取到doc元素的第一个子元素"courses",并判断它是否是一个元素(拥有属性的节点才是元素):QDomElement eleCourses = rootDoc.firstChildElement();
while (eleCourses.isElement()) {    因为我们写XML时就知道"courses"节点是有属性的,所以这里我们可以对它进行这样的判断。    然后,我们判断其标签名是否是期望的"courses",如果不是那么我们通过调用eleCourse. nextSiblingElement()直接跳到其下一个兄弟(同等级的下一个节点):strName = eleCourses.tagName();
if (strName != "courses") {
    eleCourses =eleCourses.nextSiblingElement();
    continue;
}    然后,通过QDomElement::attribute()接口读取元素的属性值:strName = "institution";
strValue = eleCourses.attribute(strName);    用相同的方法获取到"teacher"、"count"等属性值。    然后,遍历其子节点:QDomElement eleLesson = eleCourses.firstChildElement();
      while (eleLesson.isElement()) {
    strName = eleLesson.tagName();
    if (strName == "lesson") {
         ……
    }
    eleLesson =eleLesson.nextSiblingElement();
      }
    上述代码中,eleCourses.firstChildElement()得到"courses"的第一个子元素,如果eleLesson有效则进入while循环。    while循环内部首先判断eleLesson的标签是否是期望值,如果不是则跳转到下一个元素。    如果是“lesson”标签,则读取其属性值:QDomNamedNodeMap attrs =eleLesson.attributes();
   int nC = attrs.count();
   for (int i = 0; i < nC; ++i) {
    QDomAttrattrEle = attrs.item(i).toAttr();
    if (!attrEle.isNull()) {
    strName = attrEle.name();
    strValue = attrEle.value();
    }
   }
    上述代码中,eleLesson.attributes()得到一个QDomNamedNodeMap对象,该对象保存了属性、值的键值对映射。我们对其遍历,获取每一个属性名称、属性值。每一个属性对应一个QDomAttr。    然后,我们访问eleLesson的文本子节点,eleLesson.firstChild()可以获取到第一个子节点:if (!eleLesson.firstChild().isNull()) {
    dtText =eleLesson.firstChild().toCharacterData();
    if(!dtText.isNull()) {
          strValue = dtText.data();
    }
}     因为我们保存XML文件时,"lesson"元素的第一个子节点就是文本,所以我们对应这样编码是可以的。toCharacterData()将其转化为字符对象,然后调用data()就可以获取到内容了。大家试一下吧。
结语---------------------------------------------------------------------------------------------------------------------- 我们通过XML文件的保存介绍了XML文件的格式以及Qt的相关类。其实XML还有其他一些知识我们没有展开,比如css、CDATA、命名空间等内容。我们先带大家热热身,等大家可以熟练操作XML了,这些内容也就很容易理解和掌握了。使用DOM方式操作XML文件的优点是编码简单,缺点是占用内存非常多,而且也比流方式操作XML要慢,那么,您希望了解使用流方式读取XML相关的内容吗?欢迎关注《QT入门与提高-GUI产品开发》。选课后加课程的QQ群可以与更多学员交流,日常工作问题说不定可以在群中得到快速解答。
注解----------------------------------------------------------------------------------------------------------------------注1:https://www.cnblogs.com/chengdabelief/p/6816790.html

上一节:KS04-12 普通文本文件读写下一节:KS04-14 配置文件-INI格式

liudianwu 发表于 2018-12-27 13:48:33

老白要出书了,阔以阔以!

baizy77 发表于 2018-12-27 23:03:57

liudianwu 发表于 2018-12-27 13:48
老白要出书了,阔以阔以!

还在努力中,感谢支持,也欢迎批评指正。:)
页: [1]
查看完整版本: 【独家连载】Qt入门与提高:KS04-13 配置文件-XML格式