由于我们的前台使用C语言编写CGI,如果对方提供XML接口给我们传递数据,就必须有解析的程序,这也可能是今后数据接口的最通用的办法。经过研究,正如使用C语言来生成页面一样,显然使用C语言解析XML要比PHP和ASP要麻烦很多。
同其它语言一样,解析的方法一般都是调用现有的解析器,因为这样省时省力。PHP4是内置的EXPAT,PHP5是内置的LIBXML2,WIN平台可以调用MSXML。FREEBSD上使用C语言,最流行的就是调用EXPAT和LIBXML2,由于PHP基于某些原因放弃了EXPAT,所以我主要试用了LIBXML2。
LIBXML2主页是
http://xmlsoft.org安装过程:(需要ROOT权限)
gunzip -c libxml2-2.6.22.tar.gz | tar xvf -
cd libxml2-2.6.22
./configure
make
su
make install
exit
安装完成后就可以使用简单的代码解析XML文件,包括本地和远程的文件,但是在编码上有一些问题。LIBXML默认只支持UTF-8的编码,无论输入输出都是UTF-8,所以如果你解析完一个XML得到的结果都是UTF-8的,如果需要输出GB2312或者其它编码,需要ICONV来做转码(生成UTF-8编码的文件也可以用它做)。
ICONV的安装过程和LIBXML2一样。
下面是一些例子,包括解析XML和转码
# i nclude <stdio.h>
# i nclude <string.h>
# i nclude <stdlib.h>
# i nclude <libxml/xmlmemory.h>
# i nclude <libxml/parser.h>
#i nclude <iconv.h>
//***********************************************************************//
//* d_ConvertCharset: 编码转换函数,可以转换任意两种编码格式
//* ddr/2005-11-10
//* 此函数需要库libiconv,编译时需加-liconv,如果找不到库,编译时加-L/usr/local/lib
// 其中/usr/local/lib为安装库文件的目录
// 使用时需要#i nclude <iconv.h>,如果找不到此头文件请在编译时加-I/usr/local/include
// 其中/usr/local/include为安装头文件的目录
//* 需要使用static变量作为输出的缓冲区,这里设置的最大长度是1024,可以根据需要修改,以避免溢出
// 由于使用了static变量,所以这个函数是不可重入的,非线程安全的
// 可以改用new的方式来实现可重入
//***********************************************************************//
static char s_strBufOut[1024];
char *d_ConvertCharset(char *cpEncodeFrom, char *cpEncodeTo, const char *cpInput)
{
char *cpOut;
size_t iInputLen, iOutLen, iReturn;
iconv_t c_pt;
if ((c_pt = iconv_open(cpEncodeTo, cpEncodeFrom)) == (iconv_t)-1)
{
printf("iconv_open failed!\n");
return NULL;
}
iconv(c_pt, NULL, NULL, NULL, NULL);
iInputLen = strlen(cpInput) + 1;
iOutLen = 1024;
cpOut = s_strBufOut;
iReturn = iconv(c_pt, &cpInput, &iInputLen, &cpOut, &iOutLen);
if (iReturn == -1)
{
return NULL;
}
iconv_close(c_pt);
return s_strBufOut;
}
//输出每一项的内容,使用GB2312编码输出
void parseItem (xmlDocPtr doc, xmlNodePtr cur)
{
xmlChar *key;
cur = cur->xmlChildrenNode;
while (cur != NULL)
{
if ((!xmlStrcmp(cur->name, (const xmlChar *)"songname")))
{
key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
printf("songname: %s\n", d_ConvertCharset("utf-8", "gb2312", (char *)key));
xmlFree(key);
}
else if ((!xmlStrcmp(cur->name, (const xmlChar *)"songurl")))
{
key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
printf("songurl: %s\n", d_ConvertCharset("utf-8", "gb2312", (char *)key));
xmlFree(key);
}
else if ((!xmlStrcmp(cur->name, (const xmlChar *)"singer")))
{
key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
printf("singer: %s\n", d_ConvertCharset("utf-8", "gb2312", (char *)key));
xmlFree(key);
}
else if ((!xmlStrcmp(cur->name, (const xmlChar *)"singerurl")))
{
key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
printf("singerurl: %s\n", d_ConvertCharset("utf-8", "gb2312", (char *)key));
xmlFree(key);
}
cur = cur->next;
}
return;
}
void parseDoc(char *docname)
{
xmlDocPtr doc; //解析树
xmlNodePtr cur; //当前节点
doc = xmlParseFile(docname);
if (doc == NULL )
{
fprintf(stderr,"Document not parsed successfully. \n");
return;
}
//得到根节点
cur = xmlDocGetRootElement(doc);
if (cur == NULL)
{
fprintf(stderr,"empty document\n");
xmlFreeDoc(doc);
return;
}
//判断根节点是不是mp3
if (xmlStrcmp(cur->name, (const xmlChar *) "mp3"))
{
fprintf(stderr,"document of the wrong type, root node != mp3");
xmlFreeDoc(doc);
return;
}
//得到当前节点的第一个子节点,即第一个ITEM
cur = cur->xmlChildrenNode;
while (cur != NULL)
{
if ((!xmlStrcmp(cur->name, (const xmlChar *)"item")))
{
//输出每个ITEM
parseItem (doc, cur);
}
cur = cur->next;
}
xmlFreeDoc(doc);
return;
}
//入参可以是一个文件,也可以是一个URL,要求必须是UTF-8编码
int main(int argc, char **argv)
{
char *docname;
if (argc <= 1)
{
printf("Usage: %s docname\n", argv[0]);
return(0);
}
docname = argv[1];
parseDoc (docname);
return 0;
}