一.XML基础

1.XML简介

  • XML是指可扩展标记语言(Extensible Markup Language),它是一种标记语言,很类似HTML。它被设计的初衷是为了替换html,但没有替换成功,所以就退居幕后,常用作配置文件。
  • XML标签没有被预定义,需要用户自行定义标签。
  • XML技术是W3C组织(World Wide Web Consortium万维网联盟)发布的,目前遵循的是W3C组织于2000年发布的XML10规范。

2.XML常见应用

  • 用于软件/框架的配置文件
  • 保存数据
  • 传输\交换数据

二.XML文件格式

1.文档声明

XML文件的第一行必须是文档声明,即使是注释或空格等无意义的内容也会报错。

<?xml version="1.0" encoding="utf-8"?>
  • version (必须要有) :表示xml的版本1.0 1.1(要是有10,因为1.1版本不会向下兼容)
  • encoding (可选) : xml 档的编码方式 utf-8、gbk (gb2312 gb2310)、 iso8859-1(不支持中文)

2.根标签

即使你把上面的文档声明放在第一行,用浏览器打开也还是会报错,这是因为我们一般在文档声明的第二行我们一般会跟上根标签。

三.XML标签定义

1.标签定义

  • 1.XML文件只能有一个跟标签
  • 2.标签自定义,也可以是中文
  • 3.标签无论是成对的还是自闭合的,有开始也有结束

2.标签命名规范

  • 1.区分大小写,标签签后必须一致
  • 2.标签不能以数字和下划线开头
  • 3.不能以xml开始
  • 4.不能包含空格
  • 5.中间不能包含冒号

注意:XML解析的时候会把空格和换行都当作内容来处理

3.标签属性

  • 1.每个自定义标签可以有多个属性,但不能相同
  • 2.属性与属性值之间使用引号

4.注释

  • 格式:<-- xml注释 -->
  • 注释中间不能嵌套

四.补充

1.转义字符

我们知道标签是用大于和小于号定义的

<            &lt;
>            &gt;
&            &amp;
"            &quot;
'            &apos;

2.CDATA

术语 CDATA 指的是不应由 XML 解析器进行解析的文本数据(Unparsed Character Data)。

在 XML 元素中,“<” 和 “&” 是非法的。

“<” 会产生错误,因为解析器会把该字符解释为新元素的开始。

“&” 也会产生错误,因为解析器会把该字符解释为字符实体的开始。

某些文本,比如 JavaScript 代码,包含大量 “<” 或 “&” 字符。为了避免错误,可以将脚本代码定义为 CDATA。

CDATA 部分中的所有内容都会被解析器忽略。

CDATA 部分由 “<![CDATA[*" 开始,由 "*]]>” 结束:

<script>
<![CDATA[
function matchwo(a,b)
{
if (a < b && a < 0) then
  {
  return 1;
  }
else
  {
  return 0;
  }
}
]]>
</script>

五.示例分析

1.元素(标签)与属性

<person sex="female">
  <firstname>Anna</firstname>
  <lastname>Smith</lastname>
</person> 

<person>
  <sex>female</sex>
  <firstname>Anna</firstname>
  <lastname>Smith</lastname>
</person> 

在第一个例子中,sex 是一个属性。在第二个例子中,sex 则是一个子元素。两个例子均可提供相同的信息。

没有什么规矩可以告诉我们什么时候该使用属性,而什么时候该使用子元素。我的经验是在 HTML 中,属性用起来很便利,但是在 XML 中,您应该尽量避免使用属性。如果信息感觉起来很像数据,那么请使用子元素吧。

因使用属性而引起的一些问题:

  • 属性无法包含多重的值(元素可以)
  • 属性无法描述树结构(元素可以)
  • 属性不易扩展(为未来的变化)
  • 属性难以阅读和维护

请尽量使用元素来描述数据。而仅仅使用属性来提供与数据无关的信息。

2.C语言解析XML文件

使用C语言解析XML文件可以使用第三方库来完成,比如Expat和Libxml2等。

Expat是一款轻量级的、高速的XML解析器,它适用于嵌入式系统和移动设备等资源受限情况下的XML解析。使用Expat解析XML文件的基本步骤如下:

  1. 引入expat.h头文件。
  2. 创建一个XML解析器对象。
  3. 定义回调函数,用来处理XML元素和文本节点等信息。
  4. 调用XML解析器的解析函数,将XML文件以缓冲区方式传递给解析器。
  5. 处理完毕后释放解析器对象。

六.代码理解

1.源代码

#include <stdio.h>
#include <expat.h>

void start_element(void *data, const char *element, const char **attribute)
{
    printf("Start element: %s\n", element);
}

void end_element(void *data, const char *element)
{
    printf("End element: %s\n", element);
}

void handle_data(void *data, const char *content, int length)
{
    char buffer[100];
    strncpy(buffer, content, length);
    buffer[length] = '\0';
    printf("Data: %s\n", buffer);
}

int main()
{
    XML_Parser parser = XML_ParserCreate(NULL);
    XML_SetElementHandler(parser, start_element, end_element);
    XML_SetCharacterDataHandler(parser, handle_data);

    FILE *fp = fopen("example.xml", "r");
    char buf[1024];
    int len;
    while ((len = fread(buf, 1, sizeof(buf), fp)) > 0)
    {
        if (!XML_Parse(parser, buf, len, feof(fp)))
        {
            fprintf(stderr, "Parse error at line %d:\n%s\n",
                    XML_GetCurrentLineNumber(parser),
                    XML_ErrorString(XML_GetErrorCode(parser)));
            return 1;
        }
    }

    XML_ParserFree(parser);
    fclose(fp);

    return 0;
}

2.注释

以下是对每一行代码的解释:

#include <stdio.h>
#include <expat.h>

这两个语句分别引入了stdio.hexpat.h头文件,前者是标准输入输出库的头文件,后者是Expat XML解析器库的头文件。

void start_element(void *data, const char *element, const char **attribute)
{
    printf("Start element: %s\n", element);
}

定义一个回调函数start_element,用于处理XML元素开始标签。该函数接受三个参数:指向用户数据的指针、元素名称、属性数组。

void end_element(void *data, const char *element)
{
    printf("End element: %s\n", element);
}

定义一个回调函数end_element,用于处理XML元素结束标签。该函数接受两个参数:指向用户数据的指针、元素名称。

void handle_data(void *data, const char *content, int length)
{
    char buffer[100];
    strncpy(buffer, content, length);
    buffer[length] = '\0';
    printf("Data: %s\n", buffer);
}

定义一个回调函数handle_data,用于处理XML文本节点。该函数接受三个参数:指向用户数据的指针、文本内容、文本长度。

int main()
{
    XML_Parser parser = XML_ParserCreate(NULL);
    XML_SetElementHandler(parser, start_element, end_element);
    XML_SetCharacterDataHandler(parser, handle_data);

在主函数中,先创建了一个XML解析器对象parser,并通过XML_ParserCreate(NULL)函数创建。然后,分别使用XML_SetElementHandler()XML_SetCharacterDataHandler()函数设置回调函数,用于处理元素开始标签、元素结束标签和文本节点。

 FILE *fp = fopen("example.xml", "r");
    char buf[1024];
    int len;
    while ((len = fread(buf, 1, sizeof(buf), fp)) > 0)
    {
        if (!XML_Parse(parser, buf, len, feof(fp)))
        {
            fprintf(stderr, "Parse error at line %d:\n%s\n",
                    XML_GetCurrentLineNumber(parser),
                    XML_ErrorString(XML_GetErrorCode(parser)));
            return 1;
        }
    }

    XML_ParserFree(parser);
    fclose(fp);

    return 0;
}

接着打开XML文件,并使用fread()函数从文件中读取数据缓冲到buf数组中,然后调用XML_Parse()函数进行解析。如果解析出错,则输出错误信息。最后释放XML解析器对象,并关闭文件。

以上代码实现了Expat解析XML文件的基本流程,通过设置回调函数可以处理不同类型的XML节点。

注明:XML和html类似,了解html即可,作为传输数据的工具时,它又逐渐被JSON替代,所以我们只需要知道他不需要精通。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐