基于Qt命令行处理XML文件读写

165 阅读11分钟

编辑

Qt源码在后面,文本介绍Qt国际化语言和XML

XML基础(一)

## 1、概述

### 1.1 定义(xml是个啥玩意儿?)

XML(extensible Markup Language)俗称差妹儿,专业称之为:可拓展标记语言。

  • (1)何为标记,指的是一种标记语言,或者标签语言,即用一系列的标签来对数据进行描述。
  • (2)何为拓展,简单来说就是用户可以自己定义标签。

### 1.2 作用(能用来干啥?)

  • (1)作为数据传输的标准。好读,好维护,好扩展,与编程语言无关,任谁都会选择他来担任这个数据传输的角色吧。。。
  • (2)作为配置文件。其实很多软件和框架,都会提供XML文件配置的方式,以便可以方便快捷的修改软件或框架的功能。
  • (3)持久化数据。啥意思?简单来说就是可以将数据存到xml文件中,把xml当做一个临时的小型数据库。当然,重要的数据还是要存到正经的数据库中的,别问为什么,问就是xml存数据会不安全(doge)。
  • (4)简化平台变更。在系统更换平台的时候,普通的数据会存在不兼容的问题,但是XML 数据以文本格式存储,使得 XML 在不损失数据的情况下,更容易扩展和升级。简直不要太好用。

### 1.3 历史(简单讲讲XML的故事?)

最开始的标记语言是GML(通用标记语言),1969年出生的,就是用来做计算机之间的通信,通信就会传输数据。后面GML发现自己不是很好,就开始自省,完善自身,于是在1985年重获新生,改名为SGML(标准通用标记语言),也是用来通信,传输数据。随着万维网的不断发展,1993年,在SGML基础上有出现了HTML语言( HTML 超文本的标记语言(HyperText Markup Language)),这个名字应该很是耳熟吧,前端必学呀,主要是用于万维网上的页面展示。但HEML仍有不少缺陷,于是这篇博客的主角诞生了,1998年,W3C组织推出了XML(可扩展标记语言),本来是想用XML代替HTML的,但没想到事与愿违,他们之间还是有一定差别,即便现在,XML也没能完成主子给它的任务,因为HTML在整个万维网的使用太广泛了(人家早出身几年,经历的也多,人缘广泛,想强行替代别人还是有点脱离现实了)


XML和HTML对比

  • 1、XML主要用来描述数据
  • 2、HTML主要用来显示数据

## 2、XML语法

### 2.1 文档声明

XML声明文件的可选部分,如果存在,则需要放在文档的第一行。如下

<?xml version="1.0" encoding="utf-8"?>

这里描述了xml的版本以及文档所用的编码格式

### 2.2 元素

定义

元素指的就是XML中的标记,这种标记也被称为标签、节点(节点这种称呼常见于框架中)

书写规范

一个XML元素可以包含字母、数字以及其它一些可见字符,但必须遵守下面的一些规范:

  1. 不能以数字或部分标点符号开头
  2. 不能包含空格和特定的几个符号
  3. 标签必须成对出现不允许缺省结束标签
  4. 根元素有且只有一个,它是所有其他元素的父元素
  5. 大小写敏感
  6. 允许多层嵌套但是不允许交叉嵌套
<root>  <child>    <subchild>...</subchild>  </child></root>// 这里的标签就是根元素,如上
<root>  <a>www.baidu.com</a></root>// 元素是可以包含标签体的,如上
<root>  <a></a><root>// 元素也可以不包含标签体,如上
<root>  <a>  www.baidu.com  </a></root><root>  <a>www.baidu.com</a></root>// 对于XML标签中出现的空格和换行,在XML解析程序都会当作标签内容进行处理,如上面两个代码,标签中内容还是有区别的,如上

### 2.3 属性

定义

属性(Attribute)提供了元素相关的一些额外信息。

使用规范

XML 属性写在开始标签中,并且属性值必须加引号,例如,可以是双引用,也可以是单引号,如下

<root>  <person sex="female"></person></root>

一个元素可以有多个属性,它的基本格式为:<元素名 属性名=“属性值” 属性名=“属性值”>

<root>  <person sex="female" age='18' email="xxqq.com"></person></root>

元素和属性使用实例

如果有多个相同标签的时候,可以使用 id进行区别

<message>  <note id="1">    <date>      <day>10</day>      <month>02</day>      <year>2021</year>    </date>    <to>tom</to>    <from>mary</from>    <msg>love</msg>  </note>  <note id="2">    <date>      <day>10</day>      <month>02</day>      <year>2021</year>    </date>    <to>tom</to>    <from>mary</from>    <msg>together</msg>  </note></message>

### 2.4 实体

定义

一些字符拥有特殊的意义,在XML文档中,是不能直接使用的。

如果把字符 “<” 放在 XML 元素中,会发生错误,这是因为解析器会把它当作新元素的开始。

<?xml version="1.0" encoding="utf-8"?><message>if salary < 1000 then</message>

此时浏览器会报错:invalid element name

这时候,可以使用XML中预定义的实体,来代替这个特殊符号,如下

<?xml version="1.0" encoding="utf-8"?><message>if salary &lt; 1000 then</message>
预定义实体与自定义实体

预定义实体
XML中预定义实体有[你会看到Qt中的.ui文件就是XML格式的文件]

实体字符简介
& lt;<Less than
& gt;Greater than
&&mpersand
& apos;'Apostrophe
& quot;"Quotation mark

可以看到定义实体的格式:&实体名

自定义实体
格式

<!DOCTYPE 根元素名称[  <!ENTITY 实体名 实体内容>]>

例如:

<!DOCTYPE root[  <!ENTITY school "清华大学">]><root>  <name>&school;</name></roo>

浏览器显示结果如下:

编辑

### 2.5 注释

格式

<!-- 注释内容 -->

  • 1

使用注释时注意一下几点
(1)注释内容中不要出现–

<!-- -- -->

<!-- 上面的注释会报错 -->

  • 1
  • 2
  • 3

(2)不要把注释放在标签中间

(3)注释不能嵌套


## 3、CDATA

XML 解析器,会解析 XML 文档中所有的文本。当某个 XML 元素被解析时,其标签之间的文本也会被解析。

<root>   <tag>     <name>&amp;</name>     <entity>&amp;</entity>  </tag></root>

浏览器解析结果

编辑

在xml中

  • 解析器进行解析的内容,称为PCDATA(Parsed CDATA)
  • 解析器不会解析的内容,称为CDATA,(Character Data)

在一些情况下,我们在xml中编写的特殊内容,例如特殊字符、代码等,并不希望解析器去解析,而是希望把这些内容按字符串原样输出即可,不需要做额外任何解析处理。这时候,我们就可以把这些内容,写在指定的CDATA区域内即可。

CDATA区域的格式

例如:

<root>  <tag>    <name>&amp;</name>    <entity><![CDATA[&amp;]]></entity>  </tag></root>// 输出&amp;
  • XML 文档中的所有文本均会被解析器解析。只有 CDATA 区段中的文本会被解析器

## 4、XML和CSS

CSS简介

XML文件中的内容,可以配合CSS进行内容的样式渲染,例如控制字体大小、颜色等。

CSS(Cascading Style Sheets) 层叠样式表,通常用来给HTML中内容、或者XML中内容进行样式的渲染

例如:先创建一个css后缀的文件,在该文件中编写下面的代码

name{  font-size:30px;  font-weight:bold;  color:red;}name{  font-size:30px;  font-weight:bold;  color:green;}​
处理指令

处理指令,简称PI (processing instruction),可以用来指定解析器如何解析XML文档内容。
例如:在XML文档中可以使用xml-stylesheet指令,通知XML解析引擎,使用test.css文件来渲染xml内容。

<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet href="test.css" type="text/css"?> <class>  <student id="001">    <name>张三</name>    <age>20</age>  </student>  <student id="002">    <name>李四</name>    <age>20</age>  </student></class>

渲染后的效果如下:

编辑


## 5、Qt XML


XML和Qt密不可分,还记得QT的.ui文件吗,就是XML文件

未来XML的用户使用量只增不减,所以有必要记录一下。

## 5.1 .pro文件

采用的是命令行项目,没有.ui文件的

# 配置命令行cmdlineCONFIG   += cmdlineQT       -= guiSOURCES  += main.cpp​# 【1】 如果项目很大 需要添加resources_bigCONFIG += resources_big# 【2】国际化语言翻译文件绝对路径TRANSLATIONS  +=  translation/tr_zh.ts  \                  translation/tr_en.ts# installtarget.path = $$[QT_INSTALL_EXAMPLES]/xml/xmlstreamlintINSTALLS += target​

【2】是生成国际化语言的绝对路径,还需执行以下操作才能生成完整的多国翻译文件。

更新翻译:生成.ts文件

编辑

发布翻译:生成.qm文件

编辑

如果你想翻译多个国家的语言,就在对应的.ts文件修改即可

编辑

<translation type="unfinished">填写你翻译的文本</translation>

Qt国际化语言相对简单,但不是本节的重点,只是代码中有使用tr()函数,所以让大家了解一下。

## 5.2 main文件

文本项目内容不多,所以直接在main全部实现,主要是对XML文件进行读写。【从myxml.xml文件读取->写入savaXML.xml文件】

/******************************************************************************** WX公众号:Qt历险记** CSDN博客网:Qt魔术师******************************************************************************/#include <QCoreApplication>#include <QFile>#include <QStringList>#include <QTextStream>#include <QXmlStreamReader>​/* 此类的唯一目的是创建翻译上下文。【可以翻译成各个国家】*/class XmlStreamLint{public:    // 从字面意思:声明tr函数 tr()函数就是用来处理国际化语言的 因为tr()时QObject的方法QObject::tr() or QObject::trUtf8(),而增加头文件会增加编译的时间    // 这个宏的功能:XmlStreamLint可以调用以下两个函数    /*  static inline QString tr(const char *sourceText,                           const char *comment = 0);        static inline QString trUtf8(const char *sourceText,                               const char *comment = 0);    */    Q_DECLARE_TR_FUNCTIONS(XmlStreamLint) // 必须出现在类定义的最顶部};​int main(int argc, char *argv[]){    enum ExitCode    {        Success,        ParseFailure,        ArgumentError,        WriteError,        FileFailure    };​    // 打包发布时可以设置插件的目录 默认路径列表由一个条目组成,即插件的安装目录。    // 由于Qt在构造QApplication时会加载插件,因此设置此项的机会就是在调用QApplication的构造函数之前进行    QCoreApplication::addLibraryPath("pluins");​    QCoreApplication app(argc, argv);​    QTextStream errorStream(stderr); // 标准IO出错​    errorStream <<"argc = "<<argc<<Qt::endl;    errorStream <<"arg0 = "<<QCoreApplication::arguments().at(0)                <<Qt::endl<<"arg1 = "<<QCoreApplication::arguments().at(1)<<Qt::endl                <<Qt::endl<<"arg2 = "<<QCoreApplication::arguments().at(2)<<Qt::endl; // 外部参数​    // 命令行参数个数可以在编译时指定:点击最左侧项目->点击Run按钮->在运行的下面第二行:mmand linearguments:行编辑框输入参数,参数之间空格分隔,注意一个完整的参数中不能有空格不然被分成了两个参数    if (argc != 3)    {        errorStream << XmlStreamLint::tr(                       "Usage: xmlstreamlint <path to XML file>\n");        return ArgumentError;    }​    // 参数arg0是默认应用程序编译生成的.exe文件的绝对路径,arg1才是我们输入的第一个命令行参数    QString inputFilePath(QCoreApplication::arguments().at(1));    QFile inputFile(inputFilePath);​    // 文件不存在    if (!QFile::exists(inputFilePath))    {        errorStream << XmlStreamLint::tr(                       "File %1 does not exist.\n").arg(inputFilePath);        return FileFailure;​    }    // 文件打开失败    else if (!inputFile.open(QIODevice::ReadOnly)) {        errorStream << XmlStreamLint::tr(                       "Failed to open file %1.\n").arg(inputFilePath);        return FileFailure;    }​    // 写入我指定的另一个空的XML文件    QString outFilePath(QCoreApplication::arguments().at(2));    QFile outputFile(outFilePath);    if (!outputFile.open(QIODevice::WriteOnly))    {        errorStream << XmlStreamLint::tr("Failed to open " + outputFile.fileName().toUtf8());        return WriteError;    }​//! [第一步]//! // qxml流阅读器类提供了一个快速解析器,可以通过简单的流api读取格式良好的xml    QXmlStreamReader reader(&inputFile);    // qxml流媒体写入器类提供了一个具有简单流媒体api的xml写入器    QXmlStreamWriter writer(&outputFile);//! [end]​//! [第二步] 按行读取XML文件    while (!reader.atEnd())    {        reader.readNext();​        if (reader.error()) // 解析错误 只是在那行那列        {            errorStream << XmlStreamLint::tr(                           "Error: %1 in file %2 at line %3, column %4.\n").arg(                               reader.errorString(), inputFilePath,                               QString::number(reader.lineNumber()),                               QString::number(reader.columnNumber()));            return ParseFailure;//! [end]​//! [第三步]        }        else {            // 写入读者的当前状态。支持所有可能的有效状态。此函数的目的是支持对xml数据的链式处理。            writer.writeCurrentToken(reader); // 读取的内容写入到文件中        }    }//! [end]​    return Success;}​

## 5.3 运行步骤

需要先添加命令行参数,才能有效果哦,注意命令行参数之间使用空格分隔,也就是你的路径中最好不要有空格,不然就被理解成多个参数了。

点击:

编辑

编辑

文件如下:如何快速添加路径

方法1:右键属性,安全,对象名称就是绝对路径

编辑

编辑

方法2是对方法1的拓展:在Exporer中显示,其他操作如同方法1

编辑

命令行添加完毕,可以运行

编辑

运行成功

编辑

使用VScode打开两个文件看看:

编辑

==XML远远不止这些,下次继续肝==

==END==