发信人: pom (红屋骑士), 信区: XML
标 题: expat的概貌
发信站: BBS 水木清华站 (Fri Jun 8 10:19:53 2001)
expat是一个面向流的解析器。你向解析器登记回调(或处理)函数然后开始向它输入文
件。当解析器认出部分文件时,(如果你已登记),它将为那部分调用适合处理。文件是
分部分输给解析器的,所以在你有整个文件之前,你就能开始解析语法.这个也允许你解
析不适合内存的确实巨大文件的语法。
expat会由于设定你能的很多种类的处理和选项让人害怕。但是你只需了解到四个函
数,就能做80%你想做的相关工作:
XML_ParserCreate
建立一新的解析器对象。
XML_SetElementHandler
为开始和末端标签准备处理函数。
XML_SetCharacterDataHandler
为文本准备处理函数。
XML_Parse
把一充满着文件的缓冲区传给解析器。
这些函数和其他的会在这文章的参考资料部分中被描述.参考资料部分详细也描述传给不
同类型的处理函数的参数。
这子目录含有例子在这文章中应用的Makefile和源代码。
让我们考虑一非常简单例子程序,其只使用了上述函数的三个。(它不需要设定一个属性
作函数).程序outline.c打印了一个元素的轮廓,验证子元素以区分他们和其含有他们的
父母元素.开始处理函数做所有的工作。它打印每一层祖先元素的二分空间,然后它打印
元素和属性信息。最后它增加全局性深度变量。
int Depth;
void
start(void *data, const char *el, const char **attr) {
int i;
for (i = 0; i < Depth; i++)
printf(" ");
printf("%s", el);
for (i = 0; attr[i]; i += 2) {
printf(" %s='%s'", attr[i], attr[i + 1]);
}
printf("/n");
Depth++;
} /* 开始处理函数的结束 */
末端标签仅仅做减少深度的簿记工作.
void
end(void *data, const char *el) {
Depth--;
} /*末端处理函数的结束 */
在建立解析器之后,主程序对解析器仅仅有清除文件的工作,以便它能做它的工作.
编译expat
汇编时间条件句
有几个控制编译后表现的编译时间宏命令。
XML_UNICODE
内部使用UTF 16和使用UTF 16把字符串传给应用程序而不是UTF 8。这个将改变本来定
义为字符的XML_Char的类型定义。
XML_UNICODE_WCHAR_T
从<stddef.h>内部的像wchar_t一样宣布使用UTF 16。和这种方式把字符串传给应用程
序。如果它还没被设定,这个会设定XML_UNICODE。如果被设定为XML_UNICODE而不是XM
L_UNICODE_WCHAR_T时,UTF 16被存储为unsighed short。
XML_DTD
包含代码解析外部DTD的语法。
XML_NS
名域的词汇的检查。
XML_BYTE_ORDER
为little-endian机器(让第一最不重要字节的机器)设定为"12",和为big-endian(最
重要字节第一)设定为21。
XML_MIN_SIZE
产生一个更小的解析器,但是一般地说那个将运行更慢的.
如果你的系统没有memmove但是确实有bcopy,然后你将想要有宏那向bcopy redfinesmem
move。有一Makefile宏,其在例子Makefile,XP_MM中做这个的。为了生效,你将只能不注
释它的定义。
使用expat
当我提到在概述区,文件被分片输给解析器的。它完全直到调用应用文件的多少适合的
份。例子程序,line示范了这些。它传送每次传送一行给解析器然后报告启动,结束,
正文,并且处理命令事件。由交互式的键入在文件进入这程序,你可以开始获得解析器
如何在工作的感性认识。
处理文件分层结构和流导向的解析器将要求一个好的堆栈机制来看清当前上下文.例如,
回答简单的问题, "这正文属于什么元素?"要求堆栈,因为解析器可能下降进入是的当前
的孩子的其他元素,经过这正文出来。
你很有可能想保持在堆栈的东西是现在打开元素和它的属性。你在启动处理函数推信息
进堆栈和你在结束处理函数弹出它。
为一些任务,它是充分的仅仅保持有关信息什么堆栈的深度是(或将是如果你有一个。
)大纲程序显示现在的上方一例子。再一个那样的任务将是略过一个完全的元素。当你
看见你想忽略元素的启动标签时,你设忽略标志和记录那一个元素的启动深度。当结束
标签处理函数遇见相同深度,忽略的元素已经结束和标记可以被清除了。如果你根据根
元素启动在 1的常规,那么你能为跳跃标记和跳跃深度使用相同变量。
void
init_info(Parseinfo *info) {
info->skip = 0;
info->depth = 1;
/*其他的初始化在这里*/
} /* End of init_info */
void
rawstart(void *data, const char *el, const char **attr) {
Parseinfo *inf = (Parseinfo *) data;
if (! inf->skip) {
if (should_skip(inf, el, attr)) {
inf->skip = inf->depth;
}
else
start(inf, el, attr); /*这做其余的的启动处理*/
}
inf->depth++;
} /* End of rawstart */
void
rawend(void *data, const char *el) {
Parseinfo *inf = (Parseinfo *) data;
inf->depth--;
if (! inf->skip)
end(inf, el); /*这做其余的的结束处理*/
if (inf->skip == inf->depth)
inf->skip = 0;
} /* End rawend */
注意到在上述例子差别在如何深度被操纵在启动和结束处理函数。结束标签处理函数将
是镜像启动的图象标签处理函数。这对适当地模型约束是必要的。既然,在启动标签处
理函数,我们增深度后于启动的主体标签代码,然后是在结束处理函数,在主体前我们需
要操纵它 。
如果我们已决定立即在启动处理函数增加它,那么我们已不得不在最后结束处理函数递
减它.
在处理函数间通讯
为了能不用全局变量位于不同信息处理函数传送信息。你将需要定义一个数据结构来保
持共享变量。你能然后告诉expat(和 XML_SetUserData函数)传送这结构的指针给处理函
数。这典型的第一变元被大多数处理函数接受。
名域处理
元件字体和属性名那属于给名域被传送适当的处理者在展开形式。这展开形式是名域
的连结 URI,分隔符字符 (哪一个是第2变元到 XML_ParserCreateNS),并且局部名字 (i
.e.部分后于冒号)。名和未申报的前缀被穿过处理者无变化的,和前缀和冒号仍然附属。
Unprefixed属性名永不被展开,并且 unprefixed元件名进行展开他们是当在缺省的范围
名域。
你能用StartNamespaceDeclHandler为名域的启动说明和一个声明的结束范围设定处
理函数。StartNamespaceDeclHandler在启动标记处理函数前被调用和 在相应的名域的
范围结束标记前EndNamespaceDeclHandler被调用。名域启动处理函数通过前缀和名域的
URI。缺省名域说明为(xmlns='...'),前缀将是空的。在那些缺省名域没有设定的情况U
RI将是空的。名域结束处理函数仅仅得到结束范围的前缀。
这些处理函数被每个声明调用。所以如果,例如,启动标记有三名域说明,在开始标
记处理函数被调用前启动前StartNamespaceDeclHandler将被三次调用,一次为每个声明
。
字符编码
当 XML以 Unicode为基础,并且每XML处理器被要求识别 UTF-8和 UTF-16 (1和 2字节
Unicode的编码 ),其他的编码可以在 XML文档或实体被说明。作为基本文件, XML说明
可以包含的编码说明:
<?xml version="1.0" encoding="ISO-8859-2"?>
外部的分析实体可以以正文开始说明,那样看起来象仅仅是一个编码说明的XML说明:
<?xml encoding="Big5"?>
用expat,你也可以在创建解析器的时候特定一个编码.当编码信息可能来源于文件它本身
外面 (象更高级协议.)这很有用的
在expat中有四个内建的编码:
· UTF-8
· UTF-16
· ISO-8859-1
· US-ASCII
发现别的东西在编码说明中或在协议编码指定在解析器构造函数,触发调用 UnknownEnc
odingHandler。这处理函数获得传送编码名字和指针 XML_Encoding数据结构。你的处理
函数必须填满这结构和返回 1如果它知道如何处理编码. 否则处理函数将返回0。当你设
定处理函数时,处理函数也获得把指针递给任选的你可以指示的应用程序数据结构。
expat在字符编码设置限定那它能由填满 XML_Encoding结构支持.
包含文件:
1. 每ASCII字符那能出现于良好形态 XML文件必须由单字节表示,字节必须相当于它是
ASCII编码 (除$ $@/^'{}~字符之外)
2. 字符必须用 4 字节或更少编码.
3. 所有字符编码必须有少于或等于 65535 (0xFFFF Unicode)的标量值,这不适用于内
部支援为 UTF-16和 UTF-8
4. 没有字符可以被更多不同的顺序的字节编码
XML_Encoding包含整数的数组那相当于第1编码的字节顺序。如果字节值在数组为是零或
正的,那么字节是单字节编码那编码 Unicode标量值包含在数组。-1在这数组指示畸形
的字节。如果值是 -2, -3,或 -4,字节是然后第2的开始 , 3,或 4字节序列分别地。多
-字节序列被发送给在 XML_Encoding结构指出的转换函数。这函数将返回序列的Unicod
e标量值或 -1如果序列是畸形的.
一个无经验者expat用户使用者很有可能落入陷阱是虽然 expat可以接受输入在不同的编
码,它传送给处理函数的串总是编码为 UTF-8.你的应用程序要负责翻译这些串为其他的
编码.
处理外部实体的参考
expat不读或直接分析外部的实体。注意任何外部的 DTD是外部的实体的特殊的情况 。
如果你已设置非 ExternalEntityRefHandler,那么外部的实体参考被默默地忽略。否则
,它调用你的处理函数和需要读和分析外部实体的信息。
你的处理函数不是实际对分析实体负责的,但是它是责任用 XML_ExternalEntityParser
Create创建做这工作的附加的解析器.这返回例子的 XML_Parser那有处理函数和其他的
数据结构初始化自父母解析器.你然后可以使用 XML_Parse或 XML_ParseBuffer调用这解
析器.既然外部的实体我的参考其他的外部的实体,你的处理函数将作准备被递归的调用
。
解析 DTDs
为了分析参数实体,宏 XML_DTD,在expat被编译时必须被定义另外,在创造解析器后和开
始解析前,你必须用下列变元之一调用 XML_SetParamEntityParsing:
XML_PARAM_ENTITY_PARSING_NEVER
不解析参数实体或外部的分列子集
XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE
分析参数实体和外部的子集除非standalone在 XML说明中被设为"yes"。
为了读外部的子集,你也不得不象上方的描述,设置外部的实体参考处理函数。