C++ : XML 文件解析(依赖库:TinyXml)

XML

    可扩展标记语言(eXtensible Markup Language)。

  • 被设计用来传输和存储数据,不用于表现和展示数据,HTML 则用来表现数据;

  • 独立于软件和硬件的信息传输工具

  • 主要用到的有可扩展标记语言、可扩展样式语言(XSL)、XBRL和XPath等;

  • 没有预定义,需要自定义标签。

<?xml version="1.0" encoding="UTF-8"?> <!-- 声明 -->
<site> <!-- 开始标签 -->
  <name>RUNOOB</name>
  <url>https://www.runoob.com</url>
  <logo>runoob-logo.png</logo>
  <desc>编程学习网站</desc>
</site> <!-- 结束标签 -->

用途

  • 通过读取 xml 更新网页数据内容,把数据从 HTML 分离;

  • 以纯文本格式进行存储,简化数据共享;

  • 较好的兼容性,简化了数据传输和平台变更;

树结构

<?xml version="1.0" encoding="UTF-8"?>
<note> <!-- 根元素 -->
    <to>Tove</to> <!-- 子元素 -->
    <from>Jani</from>
    <heading>Reminder</heading>
    <body>Don't forget me this weekend!</body>
</note>
  • 必须包含根元素。该元素是所有其他元素的父元素;

  • 所有的元素都可以有文本内容和属性。

语法

声明

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

标签

  • 所有的 XML 元素都必须有一个关闭标签
<p>This is a paragraph.</p>
<br />
  • 对大小写敏感;

  • 属性值必须加引号;

<note date="12/11/2007">
  • 特殊字符:

以 LF 存储换行:

Windows : 回车符(CR)和换行符(LF)。

  • 预定义实体引用:
&lt ;<less than
&gt ;>greater than
&amp ;&ampersand
&apos ;'apostrophe
&quot ;"quotation mark

元素

从(且包括)开始标签直到(且包括)结束标签的部分。

命名规则

  • 可以包含字母、数字以及其他的字符;
  • 不能以数字或者标点符号开始;
  • 不能以字母 xml(或者 XML、Xml 等等)开始;
  • 不能包含空格。

可扩展

<note>
    <date>2008-01-10</date>
    <to>Tove</to>
    <from>Jani</from>
    <heading>Reminder</heading>
    <body>Don't forget me this weekend!</body>
</note>

属性

    属性(Attribute)提供有关元素的额外信息。

  • 必须加引号;
<person sex="female">
<person sex='female'>
<gangster name='George "Shotgun" Ziegler'>
<gangster name="George "Shotgun" Ziegler">
  • 不能包含多个值、树结构,不容易扩展;

  • 分配 ID 索引可用于标识 XML 元素。

验证

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE note SYSTEM "Note.dtd">

XML DTD

定义 XML 文档的结构。它使用一系列合法的元素来定义文档结构:

<!DOCTYPE note
[
<!ELEMENT note (to,from,heading,body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>
]>

XML Schema

W3C 支持一种基于 XML 的 DTD 代替者,为 XML Schema:

<xs:element name="note">

<xs:complexType>
<xs:sequence>
<xs:element name="to" type="xs:string"/>
<xs:element name="from" type="xs:string"/>
<xs:element name="heading" type="xs:string"/>
<xs:element name="body" type="xs:string"/>
</xs:sequence>
</xs:complexType>

</xs:element>

XML 解析

    实现简单的 XML 文件解析,通常是要在可读 xml 文件的解读上进行,既要对文件内容解析并序列化,同时能够在多层次解析上进行内容判断。

    首要目标就是对 xml 数据类型进行划分,竟然存在多层级的 xml 节点,那就需要进行递归解析,此时仅存在同级的节点,可利用 xml 类数组,其次是对 xml 节点的名称、属性、内容进行解析,中间还可能存在一系列的注释文本内容。

XML 类与 Attribute 属性类

数据类型

// 节点数组
list<Xml> *m_child;

// 节点名称
string *m_name;

// 节点属性,由属性名称和属性值组成
map<string, Attribute> *m_attrs;

// 节点内容,即节点的文本描述
string *m_text;

节点属性

    由于节点属性存在多种基本数据类型值,其中包括 bool、int、double、char 数组(string),

需要在基本数据类型的基础上作出总体封装,以类(Attribute)的形式将 int、bool、double、string 进行符号类型重载,同时对符号 = 进行类重载。

operator bool();
operator int();
operator double();
operator string();

Attribute &operator=(bool attr);
Attribute &operator=(int attr);
Attribute &operator=(double attr);
Attribute &operator=(const char *attr);
Attribute &operator=(const string &attr);
Attribute &operator=(const Attribute &attr);

处理数据类型数据

    明确了数据类型的定义,解析时需要对对应的数据类型进行设置(存储)和获取(解析),进行如下定义:

string getName() const; // 获取节点名称
void setName(const string &name); // 设置节点名称

string getText() const; // 获取节点文本内容
void setText(const string &text); // 设置节点文本内容

string getAttr(const string &key) const; // 获取节点名称 key 所对应的属性值
void setAttr(const string &key, const Attribute &value); // 设置节点属性名称和值

节点数组

    明确了节点数据类型,在完成解析时,判断节点的层次则需要对节点数组大小 size 进行判定,若是对节点数组进行扩展,则需要进行节点数组符号 [] 进行重载,同时包括子节点的操作:插入 append、删除 remove

// 子节点的数量
int size() const;

// 插入子节点 child
void append(const Xml &child);

// 添加指定节点
Xml &operator[](int index); // 不存在对应索引,赋值添加子节点
Xml &operator[](const char *name);
Xml &operator[](const string &name); // 不存在对应节点名称,添加子节点

// 删除对应索引、节点名称的数组节点
void remove(int index);
void remove(const char *name);
void remove(const string &name);

数组节点遍历

    明确了节点数组的插入和删除,进行节点数组序列化输出的同时,包括对节点数组的指定位置进行遍历,通常有数组开头 begin、数组末尾 *end *,通过节点数组容器 iterator 的类型定义,完成更简洁的遍历操作:

typedef std::list<Xml>::iterator iterator; // 节点数组容器
iterator begin(); // 节点数组开头
iterator end(); // 节点数组末尾

Parser 解析类

    明确了 xml 类与 Attribute 属性类的组成和处理操作,接下来是对数据节点内容的整体解析,由于文本的整体性,需要进行节点细分,首先是进行数据字符判断,一个是文本,即字符串内容(包括文件流),另一个是索引,即字符索引:

string m_str; // 字符串文本,xml 文本数据
int m_index; // 字符索引,检索当前字符

// 加载文件路径进行 xml 文件流 file 转换成字符串 m_str
bool loadFile(const string &file);
// 指定字符串内容 str 进行加载存入字符串文本 m_str
bool loadStr(const string &str);

parse 解析

    明确了解析所需要的数据类型,接下来将对字符串文本 m_str 进行解析,通过解析 parse 函数进行对 xml 文件的声明 declaration 进行 parse_declaration 声明解析,对注释文本 comment 进行 parse_comment 注释解析,最后便是对节点 element 进行 parse_element 节点解析,其中包括对常用符号:空格、换行符、回车符、制表符进行 skip_whitespace 特殊处理:

Xml parse(); // xml 解析

void skip_whitespace(); // 空格特殊处理
bool parse_declaration(); // 声明解析
bool parse_comment(); // 注释解析
Xml parse_element(); // 节点解析

节点解析

    对 xml 文件解析头部定义:声明、注释明确之后,便是对节点进行解析,其中就包括节点名称 parse_element_Name 解析,节点文本内容 parse_element_Text 解析,节点属性解析:节点属性名称 parse_element_attrKey 解析,节点属性值 parse_element_attrValue 解析,当然还包括对属性的文本内容进行解析,以及对节点注释、子节点进行再次解析:

// 节点文本内容解析
string parse_element_Text();
// 节点名称解析
string parse_element_Name();
// 节点属性名称解析
string parse_element_attrKey();
// 节点属性值解析
string parse_element_attrValue();

Demo

2782694792/xmlParser: xml 可扩展文件的解析处理 (github.com)

xmlParser: xml 可扩展文件的解析处理 (gitee.com)

三方库

xml解析库对比总结(解析方式,第三方库,TinyXml)_惊鸿一博的博客-CSDN博客

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
XML文件解析可以用许多编程语言实现,包括C语言。下面是一个简单的C程序,可以读取和解析XML文件。 1. 首先需要引入标准头文件和libxml2文件: ``` #include <stdio.h> #include <libxml/parser.h> #include <libxml/tree.h> ``` 2. 然后定义一个函数,用来读取XML文件解析: ``` void parse_xml_file(const char* filename) { xmlDocPtr doc; xmlNodePtr cur; doc = xmlParseFile(filename); if (doc == NULL) { fprintf(stderr, "Failed to parse XML file %s\n", filename); return; } cur = xmlDocGetRootElement(doc); if (cur == NULL) { fprintf(stderr, "Empty XML file %s\n", filename); xmlFreeDoc(doc); return; } if (xmlStrcmp(cur->name, (const xmlChar *)"root")) { fprintf(stderr, "Invalid XML file %s, root node not found\n", filename); xmlFreeDoc(doc); return; } cur = cur->xmlChildrenNode; while (cur != NULL) { if ((!xmlStrcmp(cur->name, (const xmlChar *)"node"))) { xmlChar* key = xmlGetProp(cur, (const xmlChar *)"key"); xmlChar* value = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); printf("Key: %s, Value: %s\n", key, value); xmlFree(key); xmlFree(value); } cur = cur->next; } xmlFreeDoc(doc); } ``` 3. 最后在主函数中调用该函数,并传入要解析XML文件名: ``` int main() { const char* filename = "example.xml"; parse_xml_file(filename); return 0; } ``` 在这个例子中,XML文件的格式如下: ``` <root> <node key="key1">value1</node> <node key="key2">value2</node> </root> ``` 解析XML文件后,程序将输出: ``` Key: key1, Value: value1 Key: key2, Value: value2 ``` 这就完成了一个简单的XML文件解析程序。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值