一.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.转义字符
我们知道标签是用大于和小于号定义的
< <
> >
& &
" "
' '
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文件的基本步骤如下:
- 引入expat.h头文件。
- 创建一个XML解析器对象。
- 定义回调函数,用来处理XML元素和文本节点等信息。
- 调用XML解析器的解析函数,将XML文件以缓冲区方式传递给解析器。
- 处理完毕后释放解析器对象。
六.代码理解
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.h
和expat.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替代,所以我们只需要知道他不需要精通。