使用expat开发xml分析器

expat下载地址:http://sourceforge.net/projects/expat/
互联网发得展很快,都是源自于使用了超文本的表达方式。比如你查看一篇文章,看到不懂的关键字,就可以通过链接去查看它的内容,看完之后再回来接着看原来的东西,这样比较适合学习的方式。使用HTML标记的文本,是结构化储存的,这样的表达方式才可以实现超级连接。由于HTML具有超强的表达能力,也就在互联网上生存下来,那么人们就会想到能不能使用这种方式来保存所有需要保存的内容呢?慢慢地就开发XML标记语言,用来保存任意想保存的内容。由于XML具有HTML同样的功能,并且不限定标记,这样就可以表达所有的东西了。并且XML是基于树形结构的,想表达的信息就可以采用归类树的方式来组织内容了,这样能产生灵活可变的内容管理方式。比如在第二人生里采用参数配置文件,也是选择XML来保存的,并且使用expat的XML解析器来实现这方面的内容。接着下来,我们就来了解一下expat是什么东东,又是怎么样调用它来解析XML文件的。
 
expat是使用C编写的XML解释器,采用流的方式来解析XML文件,并且基于事件通知型来调用分析到的数据,并不需要把所有XML文件全部加载到内存里,这样可以分析非常大的XML文件。由于expat库是由XML的主要负责人James Clark来实现的,因此它是符合W3C的XML标准的。
 
使用expat库是非常简单的,只需要了解四个函数,就可以达到80%的功能了,看来设计这个库还是比较好的。那么需要了解那四个函数呢?这四个函数如下:
XML_ParserCreate 创建一个XML分析器。
XML_SetElementHandler 设置处理标记开始和结束的处理函数。
XML_SetCharacterDataHandler 设置处理不同字符集的数据。
XML_Parse 分析给出的缓冲区XML数据。
通过调用上面四个函数就可以实现expat调用了,使用它就是这么方便简单的。
CODE

//无双 loveunix.net 转载请保留出处 #include #include #include #include #include #include using namespace std; #define XML_MMSC_LISTEN_PORT "MMSC_LISTEN_PORT" #define XML_CLIENT_LISTEN_PORT "CLIENT_LISTEN_PORT" #define XML_MMSC_FROMADDR "MMSC_FROMADDR" #define XML_MMSC_IPADDR "MMSC_IPADDR" #define XML_MMSC_ROOT "MMSC_ROOT" #define XML_MMSC_PORT "MMSC_PORT" #define XML_MMSC_LOGIN_NAME "MMSC_LOGIN_NAME" #define XML_MMSC_LOGIN_PWD "MMSC_LOGIN_PWD" #define XML_MMSC_VASP_ID "MMSC_VASP_ID" #define XML_MMSC_VAS_ID "MMSC_VAS_ID" #define XML_MMS_SUBMIT_REPEATTIME "MMS_SUBMIT_REPEATTIME" enum CONF_TYPE{EM_UNUSE = 0, EM_LSN_MMSC_PORT,EM_LSN_CLN_PORT,EM_SENDREPEAT, EM_MMSC_IP,EM_MMSC_PORT,EM_MMSC_ROOT, EM_FROM,EM_VASPID,EM_VASID, EM_AUTHNAME,EM_AUTHPWD }; struct XML_MMSCConfInfo{ int Depth; int Element; int MMSC_LISTEN_PORT; int CLIENT_LISTEN_PORT; char MMSC_FROMADDR[50]; char MMSC_IPADDR[16]; char MMSC_ROOT[255]; int MMSC_PORT; char MMSC_LOGIN_NAME[255]; char MMSC_LOGIN_PWD[255]; char MMSC_VASP_ID[255]; char MMSC_VAS_ID[255]; int MMS_SUBMIT_REPEATTIME; }; static int GetID(const char*name){ if(!stricmp(name,XML_MMSC_LISTEN_PORT)) return EM_LSN_MMSC_PORT; if(!stricmp(name,XML_CLIENT_LISTEN_PORT)) return EM_LSN_CLN_PORT; if(!stricmp(name,XML_MMSC_FROMADDR)) return EM_FROM; if(!stricmp(name,XML_MMSC_IPADDR)) return EM_MMSC_IP; if(!stricmp(name,XML_MMSC_ROOT)) return EM_MMSC_ROOT; if(!stricmp(name,XML_MMSC_PORT)) return EM_MMSC_PORT; if(!stricmp(name,XML_MMSC_LOGIN_NAME)) return EM_AUTHNAME; if(!stricmp(name,XML_MMSC_LOGIN_PWD)) return EM_AUTHPWD; if(!stricmp(name,XML_MMSC_VASP_ID)) return EM_VASPID; if(!stricmp(name,XML_MMSC_VAS_ID)) return EM_VASID; if(!stricmp(name,XML_MMS_SUBMIT_REPEATTIME))return EM_SENDREPEAT; return EM_UNUSE; } static int SetElementValue(XML_MMSCConfInfo& Conf,const char*Value) { bool HasQoute = false; const char*pstart = strchr(Value,'"'); const char*pend; if(!pstart){ pstart = Value; pend = pstart; } else{ pend = strchr( ++ pstart,'"'); if( !pend ) return -1; HasQoute = true; } int len = pend - pstart; switch(Conf.Element){ case EM_LSN_MMSC_PORT: Conf.MMSC_LISTEN_PORT = atoi(pstart); break; case EM_LSN_CLN_PORT: Conf.CLIENT_LISTEN_PORT = atoi(pstart); break; case EM_SENDREPEAT: Conf.MMS_SUBMIT_REPEATTIME=atoi(pstart); break; case EM_MMSC_IP: if( !HasQoute) return -1; strncpy(Conf.MMSC_IPADDR,pstart,len>sizeof(Conf.MMSC_IPADDR) ?sizeof(Conf.MMSC_IPADDR):len); break; case EM_MMSC_PORT: Conf.MMSC_PORT = atoi(pstart); break; case EM_MMSC_ROOT: if( !HasQoute) return -1; strncpy(Conf.MMSC_ROOT,pstart,len>sizeof(Conf.MMSC_ROOT)? sizeof(Conf.MMSC_ROOT):len); break; case EM_FROM: if( !HasQoute) return -1; strncpy(Conf.MMSC_FROMADDR,pstart,len>sizeof(Conf.MMSC_FROMADDR)? sizeof(Conf.MMSC_FROMADDR):len); break; case EM_VASPID: if( !HasQoute) return -1; strncpy(Conf.MMSC_VASP_ID,pstart,len>sizeof(Conf.MMSC_VASP_ID)? sizeof(Conf.MMSC_VASP_ID):len); break; case EM_VASID: if( !HasQoute) return -1; strncpy(Conf.MMSC_VAS_ID,pstart,len>sizeof(Conf.MMSC_VAS_ID)? sizeof(Conf.MMSC_VAS_ID):len); break; case EM_AUTHNAME: if( !HasQoute) return -1; strncpy(Conf.MMSC_LOGIN_NAME,pstart, len>sizeof(Conf.MMSC_LOGIN_NAME)? sizeof(Conf.MMSC_LOGIN_NAME):len); break; case EM_AUTHPWD: if( !HasQoute) return -1; strncpy(Conf.MMSC_LOGIN_PWD,pstart, len>sizeof(Conf.MMSC_LOGIN_PWD)? sizeof(Conf.MMSC_LOGIN_PWD):len); break; default: break; }; return 0; } static void XMLCALL xmlstart(void *data, const char *el, const char **attr) { XML_MMSCConfInfo* pmmscinf = (XML_MMSCConfInfo*) data; for(int i = 0;attr[i];i++){ pmmscinf->Element = GetID(attr[i]); SetElementValue(*pmmscinf,attr[i+1]); } pmmscinf->Element = GetID(el); pmmscinf->Depth ++; } static void XMLCALL xmlend(void *data, const char *el) { ((XML_MMSCConfInfo*)data)->Element = EM_UNUSE; ((XML_MMSCConfInfo*)data)->Depth --; } static void XMLCALL parsedata(void *userData,const XML_Char *s,int len) { string str; str.assign(s,len); SetElementValue(*(XML_MMSCConfInfo*)userData,str.c_str()); } /** * @brief 解析MMSC配置文件 * * @return -1失败0成功 **/ static int ParseMMSCConf( XML_MMSCConfInfo& Conf ,const char*FileName) { memset(&Conf,0,sizeof(XML_MMSCConfInfo)); ifstream ifs(FileName,ios::in|ios::binary); if(!ifs) return -1; char* buf; int len; ifs.seekg(0,ios::end); len = ifs.tellg(); ifs.seekg(0,ios::beg); buf = new char[len]; if(buf) ifs.read(buf,len); ifs.close(); if(!buf) return -1; int done = 0; int err = 0; XML_Parser parser = XML_ParserCreate(NULL); if( !parser ){ cerr<<"Couldn't allocate memory for parser"<
xml文件内容如下
CODE

8801 8902 "+8613821113111/TYPE=PLMN" "172.16.65.187" "/" 9000 "nan" "whuang" "999999" "9999" 3
使用expat时不会帮助检查xml语法 所以你必须保证要分析的xml文件是对的
expat默认只支持
UTF-8
UTF-16
ISO-8859-1
US-ASCII


其它的字符集需要自己定义
UnknownEncodingHandler
的实现

不然分析器会报错
中文版
中文xml文件的处理 或是其它非标准编码xml文件的处理 expat内建只支持utf-8 ucs2 us-ascii iso8859-1 编码 其它的编码必须设置XML_SetUnknownEncodingHandler CODE /* * ===================================================================================== * * Filename: getinfo.cpp * * Description: 使用expat开发xml例子程序,包括对中文xml的支持 * * Version: 1.0 * Created: 2004-03-17 15:17:34 中国标准时间 * Revision: none * * Author: 无双[[email protected]] * Company: www.loveunix.net * * ===================================================================================== */ #include #include #include #include #include #include #include using namespace std; #define XML_MMSC_LISTEN_PORT "MMSC_LISTEN_PORT" #define XML_CLIENT_LISTEN_PORT "CLIENT_LISTEN_PORT" #define XML_MMSC_FROMADDR "MMSC_FROMADDR" #define XML_MMSC_IPADDR "MMSC_IPADDR" #define XML_MMSC_ROOT "MMSC_ROOT" #define XML_MMSC_PORT "MMSC_PORT" #define XML_MMSC_LOGIN_NAME "MMSC_LOGIN_NAME" #define XML_MMSC_LOGIN_PWD "MMSC_LOGIN_PWD" #define XML_MMSC_VASP_ID "MMSC_VASP_ID" #define XML_MMSC_VAS_ID "MMSC_VAS_ID" #define XML_MMS_SUBMIT_REPEATTIME "MMS_SUBMIT_REPEATTIME" enum CONF_TYPE{EM_UNUSE = 0, EM_LSN_MMSC_PORT,EM_LSN_CLN_PORT,EM_SENDREPEAT, EM_MMSC_IP,EM_MMSC_PORT,EM_MMSC_ROOT, EM_FROM,EM_VASPID,EM_VASID, EM_AUTHNAME,EM_AUTHPWD }; struct XML_MMSCConfInfo{ int Depth; int Element; int MMSC_LISTEN_PORT; int CLIENT_LISTEN_PORT; char MMSC_FROMADDR[50]; char MMSC_IPADDR[16]; char MMSC_ROOT[255]; int MMSC_PORT; char MMSC_LOGIN_NAME[255]; char MMSC_LOGIN_PWD[255]; char MMSC_VASP_ID[255]; char MMSC_VAS_ID[255]; int MMS_SUBMIT_REPEATTIME; }; static int ConvertFromUTF8(char*strout,int stroutlen, const char*Text,int TextLen) { // 转换utf-8编码到gb2312,linux下使用iconv函数可以直接转换, // 在win下需要先转换到ucs2再从ucs2转换到utf-8 WCHAR WCharBuf[_MAX_PATH]; LPWSTR pTransBuf = WCharBuf; int TransBufLen = _MAX_PATH; if(TextLen>=_MAX_PATH){ TransBufLen = TextLen; pTransBuf = new WCHAR[TransBufLen]; if(!pTransBuf) return 0; } int length = MultiByteToWideChar(CP_UTF8,0,Text,TextLen, pTransBuf,TransBufLen); length = WideCharToMultiByte(CP_ACP,0,pTransBuf,length, strout,stroutlen,NULL,NULL); if(pTransBuf !=WCharBuf) delete[] pTransBuf; return length; } static int GetID(const char*name){ if(!stricmp(name,XML_MMSC_LISTEN_PORT)) return EM_LSN_MMSC_PORT; if(!stricmp(name,XML_CLIENT_LISTEN_PORT)) return EM_LSN_CLN_PORT; if(!stricmp(name,XML_MMSC_FROMADDR)) return EM_FROM; if(!stricmp(name,XML_MMSC_IPADDR)) return EM_MMSC_IP; if(!stricmp(name,XML_MMSC_ROOT)) return EM_MMSC_ROOT; if(!stricmp(name,XML_MMSC_PORT)) return EM_MMSC_PORT; if(!stricmp(name,XML_MMSC_LOGIN_NAME)) return EM_AUTHNAME; if(!stricmp(name,XML_MMSC_LOGIN_PWD)) return EM_AUTHPWD; if(!stricmp(name,XML_MMSC_VASP_ID)) return EM_VASPID; if(!stricmp(name,XML_MMSC_VAS_ID)) return EM_VASID; if(!stricmp(name,XML_MMS_SUBMIT_REPEATTIME))return EM_SENDREPEAT; return EM_UNUSE; } static int SetElementValue(XML_MMSCConfInfo& Conf,const char*Value) { bool HasQoute = false; const char*pstart = strchr(Value,'"'); const char*pend; if(!pstart){ pstart = Value; pend = pstart; } else{ pend = strchr( ++ pstart,'"'); if( !pend ) return -1; HasQoute = true; } int len = pend - pstart; switch(Conf.Element){ case EM_LSN_MMSC_PORT: Conf.MMSC_LISTEN_PORT = atoi(pstart); break; case EM_LSN_CLN_PORT: Conf.CLIENT_LISTEN_PORT = atoi(pstart); break; case EM_SENDREPEAT: Conf.MMS_SUBMIT_REPEATTIME=atoi(pstart); break; case EM_MMSC_IP: if( !HasQoute) return -1; strncpy(Conf.MMSC_IPADDR,pstart,len>sizeof(Conf.MMSC_IPADDR) ?sizeof(Conf.MMSC_IPADDR):len); break; case EM_MMSC_PORT: Conf.MMSC_PORT = atoi(pstart); break; case EM_MMSC_ROOT: if( !HasQoute) return -1; strncpy(Conf.MMSC_ROOT,pstart,len>sizeof(Conf.MMSC_ROOT)? sizeof(Conf.MMSC_ROOT):len); break; case EM_FROM: if( !HasQoute) return -1; strncpy(Conf.MMSC_FROMADDR,pstart,len>sizeof(Conf.MMSC_FROMADDR)? sizeof(Conf.MMSC_FROMADDR):len); break; case EM_VASPID: if( !HasQoute) return -1; strncpy(Conf.MMSC_VASP_ID,pstart,len>sizeof(Conf.MMSC_VASP_ID)? sizeof(Conf.MMSC_VASP_ID):len); break; case EM_VASID: if( !HasQoute) return -1; strncpy(Conf.MMSC_VAS_ID,pstart,len>sizeof(Conf.MMSC_VAS_ID)? sizeof(Conf.MMSC_VAS_ID):len); break; case EM_AUTHNAME: if( !HasQoute) return -1; strncpy(Conf.MMSC_LOGIN_NAME,pstart, len>sizeof(Conf.MMSC_LOGIN_NAME)? sizeof(Conf.MMSC_LOGIN_NAME):len); break; case EM_AUTHPWD: if( !HasQoute) return -1; strncpy(Conf.MMSC_LOGIN_PWD,pstart, len>sizeof(Conf.MMSC_LOGIN_PWD)? sizeof(Conf.MMSC_LOGIN_PWD):len); break; default: break; }; return 0; } /*---------------------------------------------------------------------- * * xml解析函数, * 以下参数说明 * data是使用XML_SetUserData设置的参数,expat不进行处理,会把它交给用户回调函数处理 * el是元素名 * attr是属性-值列表,样子为attr[0]=attr[1],最后一个是NULL * *----------------------------------------------------------------------*/ static void XMLCALL xmlstart(void *data, const char *el, const char **attr) { // 当碰到xml元素的开始标志时会调用这个函数,可以看打印显示的结果 printf("start element:
sax原理
sax是Simple API for XML
Megginson采用Java语言开发的,之后SAX很快在Java开发者中流行起来。SAN项目现在负责管理其原始API的开发工作,这是一种公开的、开放源代码软件。不同于其他大多数XML标准的是,SAX没有语言开发商必须遵守的标准SAX参考版本。因此,SAX的不同实现可能采用区别很大的接口。不过,所有的这些实现至少有一个特性是完全一样的,这就是事件驱动。
事件驱动的文档解析

在SAX解析器装载XML文件时,它遍历文件文档并在其主机应用程序中产生事件(经由回调函数、指派函数或者任何可调用平台完成这一功能)表示这一过程。这样,编写SAX应用程序就如同采用最现代的工具箱编写GUI程序。

大多数SAX实现都会产生以下若干类型的事件:

*
在文档的开始和结束时触发文档处理事件。
*
在文档内每一XML元素接受解析的前后触发元素事件。任何元数据通常都由单独的事件交付。
*
在处理文档的DTD或Schema时产生DTD或Schema事件。
*
错误事件用来通知主机应用程序解析错误。

显而易见,在处理文档时你最关心的就是元素事件了。通常,SAX解析器会向你的主机应用程序提供包含元素信息的事件参数;在最低程度下也会提供元素的名字。具体取决于你的特定实现,可以定义不同类型的元素事件代表不同类型元素的处理。例如,注释元素(它可能包含主机应用程序的处理指令)就经常在接受处理时产生特殊的事件。

你可能感兴趣的:(使用expat开发xml分析器)