The Expat XML Parser

http://expat.sourceforge.net/

 

http://packages.debian.org/sid/hurd-i386/text/expat

 

http://expat.sourceforge.net/

 

如今人人鼓吹XML是Web开发者最好的朋友,有了XML的帮忙,后者即可轻松地格式化和显示来自几乎任何数据源的数据。但是,对动态内容而言,格式优良的数据却还远远谈不上达到理想状态。大多数的Web开发者都会告诉你,今天的网络上没有动态内容怎么能行!问题是:“到底该如何用XML创建动态内容呢?”


答案是用动态内容处理语言来解析XML,比如用PHP或者Perl等,从理论上说,这类程序语言是可以出于各种目的利用XML的。无非是采用一些能解析XML的工具包而已。James Clark就提供了一种名叫expat的工具包。expat XML工具包用C语言解析XML,令PHP与XML轻松共舞。

PHP是一种专为Web设计的绝佳脚本语言。XML是表示Web内容的标准。两者联手真是要多美有多美!

下面我会给读者演示一个简单的示例,通过这个例子即可说明如何用PHP把XML文档解析为HTML。然后我会介绍一些PHP的其他XML概念。用PHP解析XML很简单,操作起来很直观但却需要对细节有所解释。一旦真正掌握了应用的要领,你肯定会惊奇自己怎么不早想到把它们俩拢一块儿来。


概述
PHP用expat这种XML工具包,通过C语言来解析XML。这个工具包的函数集同Perl XML解析所采用的函数集是一样的,此外,这种工具包还是事件驱动型的解析器。这就是说,expat把每个XML标签或者新一行代码当作事件的起始,而事件就是函数的触发器。Expat的安装非常简单,如果你正在用着Apache Web服务器,那么你可以在PHP XML参考页上找到安装和下载指南。

用PHP解析XML的基本任务是这样的:首先,创建XML解析器的一个实例。接着,定义处理触发事件的函数,比如开始或者结束标签等。随后,定义实际意义的数据处理程序。最后,打开XML文件,读取文件数据并解析数据。之后关闭文件释放XML解析器。

你瞧,就像我说的那样,这套操作过程没什么特别的。不过,在我们讨论具体的示例之前先了解以下的一些警示:

Expat不对XML进行检验。这意味着只要XML文件格式正确——所有的元素嵌套得当、开始和关闭标签没有任何错误——它就会被解析。Expat可不管XML是否遵守XML文件头中引用的标准或者定义。
Expat把XML标签全部转换为大写字母。如果你的脚本在标签名和其他内容上大小写字母混用可就要小心了。
PHP是在magic quotes设置启用的情况下编译而成,那么复杂的XML文件不会得到正确的解析。要是magic quotes不是默认设置,你就当我没说。
好了,我们现在就来看看有关的示例!



基本示例

为了把复杂的事情简单化,我在例子中省略了错误检查和其他一些不必要的东西,当然,你可以在自己的代码随心所欲。我假定你早就熟悉PHP及其语法,而我会对XML函数做一番解释。首先我会说明脚本程序的含义,接着定义用户定义函数,实际上这些函数位于引用它们的代码之前。相关附件:程序清单A 所示为脚本的完整代码,脚本要解析的XML文档则是 相关附件:程序清单B。处理之后的输出结果如表A所示。

XML Articles

"Remedial XML for programmers: Basic syntax" In this first installment in a three-part series, I'll introduce you to XML and its basic syntax.

"Remedial XML: Enforcing document formats with DTDs" To enforce structure requirements for an XML document, you have to turn to one of XML's attendant technologies, data type definition (DTD).

"Remedial XML: Using XML Schema" In this article, we'll briefly touch on the shortcomings of DTDs and discuss the basics of a newer, more powerful standard: XML Schemas.

"Remedial XML: Say hello to DOM" Now it's time to put on your programmer's hat and get acquainted with Document Object Model (DOM), which provides easy access to XML documents via a tree-like set of objects.

"Remedial XML: Learning to play SAX" In this fifth installment in our Remedial XML series, I'll introduce you to the SAX API and provide some links to SAX implementations in several languages.



表A PHP解析XML的输出结果

首先我创建了XML解析器的一个实例:
$parser = xml_parser_create();

接着,我定义解析器遭遇开始和结束标签时的操作。注意“startElement”和“endElement”是用户定义的函数,当然你可以按照自己的喜好给它们起个其他名字,但我起的这些名字可是标准的习惯要求。
xml_set_element_handler($parser, “startElement”, “endElement”);

然后我定义了数据操作。这里的“characterData”也是用户定义的函数,名字也是习惯性的。
xml_set_character_data_handler($parser, “characterData”);

现在打开文件读取数据。你可以在这里开始编写错误处理代码,我在例子中省略了这些错误处理。不要忘了在脚本的开头定义$xml_file。
$filehandler = fopen($xml_file, “r”);

我开始读取文件内容,一次读4K字节并放在变量“$data”内直到文件末尾。我用xml_parse解析读取的这些数据段。
while ($data = fread($filehandler, 4096)) {
xml_parse($parser, $data, feof($filehandler));
}

最后进行清空、关闭文件和释放解析器等操作。
fclose($filehandler);
xml_parser_free($parser);

以上就是脚本中所用到的所有XML函数,下面我再具体解释下其中用到的3个用户定义函数,它们就是“startElement”、“endElement”和“characterData”。


只要xml_parse遇到<url>这样的开始标签,“startElement”函数就由XML解析器调用,在我们的例子中解析器就是$parser。该函数是必须定义的函数,它拥有3个自动传递给它的参数:XML解析器实例、大写的元素名称,比如URL以及该元素所具有的属性数组。在以上示例中,XML文件中的元素没有属性集合,所以数组参数为空,但这个参数还是必须存在的。

就这个例子而言,我决定在一个HTML表中显示我的XML数据。如上所示,出于简化的缘故我没有编写错误处理代码。这里我耍了个障眼法,因为我知道XML文件中标签出现的顺序。否则我可以用“startElement”、“characterData”和“endElement”函数来定义数组,然后用单独的函数显示结果。

function startElement($parser_instance, $element_name, $attrs) {
switch($element_name) {
case “URL” : echo “<tr><td><a href=””;
break;
case “SUMMARY” : echo “<td>”;
break;
}
}


处理元素标签之后,xml_parse一旦遇到XML数据就会调用“characterData”函数。这个函数也是由解析器自动调用的,该函数需要两个参数,解析器实例和包含数据的字符串。
function characterData($parser_instance, $xml_data) {
echo $xml_data;
}

最后xml_parse遭遇结束标签,运行“endElement”函数,该函数带两个参数,解析器实例和元素名称。
function endElement($parser_instance, $element_name) {
switch($element_name) {
case “URL” : echo “”>”;
break;
case “TITLE” : echo “</a></td>”;
break;
case “SUMMARY” : echo “</td></tr>”;
break;
}
}


用PHP解析XML的轻量锻炼到这里就结束了,下面我们开始加些重活。

增加函数

PHP中还有其他一些同XML解析有关的函数。PHP.net文档对这些函数进行了完整的说明。我在这里提到了一些,你或许不久就会用到这些函数:

xml_set_default_handler()—该函数的工作方式和xml_set_character_data_handler()函数相近,但它捕获定义的一切。这个函数常用于文档类型声明控制数据的处理。
xml_parser_set_option()—你可以用这个函数禁用字母的转大写操作或者选择其他替代的字符编码集。
xml_parse_into_struct()—这个函数用来略过“startElement”、“characterData”和“endElement”函数的调用,而把数据直接放进数组。
xml_error_string()—这个函数用来从xml_parser() 错误中获取文本信息。
xml_get_error_code()—你可以用这个函数获取上面提到的错误字符串。后两个函数的用法是这样的:if(!xml_parse($parser, $data, feof($filehandler))) { die(xml_error_string(xml_get_error_code($parser)); )
如果你觉得自己已经上手,我建议你仔细读读PHP手册页中提供的XML External Entity Example。这些例子提出了本文没有提到的其他一些概念以及处理文件错误的某些技术。


小结
本文演示了PHP和XML的紧密结合过程。两种技术以Web为中心的天性促使它们联合起来成为动态内容所需的理想解决方案。

 



本版版主招募中

<script type=text/javascript> function fastreply(subject) { if($('postform')) { $('postform').subject.value = subject; $('postform').message.focus(); } } </script>
标题: [原创]使用expat开发xml分析器
无双
荣誉斑竹
Rank: 14 Rank: 14 Rank: 14 Rank: 14
天才猪



UID 4
精华 84
积分 5863
帖子 11390
活跃指数 0
LU金币 4248 个
LU金条 0 个
阅读权限 200
注册 2003-9-16
来自 杭州
#1
 
发表于 2004-3-16 19:22  资料  个人空间  主页 短消息  加为好友 
CODE

//无双 loveunix.net 转载请保留出处
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <cstring>
#include <string>

#include <expat.h>
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"<<endl;
      return -1;
  }

  XML_SetElementHandler(parser,xmlstart,xmlend);
  XML_SetUserData(parser,&Conf);
  XML_SetCharacterDataHandler(parser,parsedata);

  if (XML_Parse(parser, buf, len, done)
          == XML_STATUS_ERROR) {
      cerr<<XML_ErrorString(XML_GetErrorCode(parser))<<" at line "
          <<XML_GetCurrentLineNumber(parser)<<endl;
      err = 1;
  }

  ifs.close();
  XML_ParserFree(parser);
  return err;
}

static Serialize(const XML_MMSCConfInfo& Conf)
{

  printf("XML_MMSCConfInfo is:/n"
          "/tDepth:%d/tElement:%d/n"
          "/tMSC_LISTEN_PORT:%d/tCLIENT_LISTEN_PORT:%d/n"
          "/tMMSC_FROMADDR:[%s]  MMSC_IPADDR:[%s]  tMMSC_PORT:%d/n"
          "/tMMSC_ROOT:[%s]/tMSC_LOGIN_NAME:[%s]/tMMSC_LOGIN_PWD:[%s]/n"
          "/tMMSC_VASP_ID:[%s]/tMMSC_VAS_ID:[%s]/tMMS_SUBMIT_REPEATTIME:%d/n",
          Conf.Depth,
          Conf.Element,
          Conf.MMSC_LISTEN_PORT,
          Conf.CLIENT_LISTEN_PORT,
          Conf.MMSC_FROMADDR,
          Conf.MMSC_IPADDR,
          Conf.MMSC_PORT,
          Conf.MMSC_ROOT,
          Conf.MMSC_LOGIN_NAME,
          Conf.MMSC_LOGIN_PWD,
          Conf.MMSC_VASP_ID,
          Conf.MMSC_VAS_ID,
          Conf.MMS_SUBMIT_REPEATTIME);
};


void main()
{
test();
   XML_MMSCConfInfo   Conf;
   ParseMMSCConf(Conf,"conf.xml");    
   Serialize(Conf);
}





不要问我结果 我只研究过程与思路
无双客栈
顶部
无双
荣誉斑竹
Rank: 14 Rank: 14 Rank: 14 Rank: 14
天才猪



UID 4
精华 84
积分 5863
帖子 11390
活跃指数 0
LU金币 4248 个
LU金条 0 个
阅读权限 200
注册 2003-9-16
来自 杭州
#2
 
发表于 2004-3-16 19:26  资料  个人空间  主页 短消息  加为好友 
这是我比较早以前写的

代码里面可能有些小错误

不过基本的xml分析器都已经实现了

xml文件内容如下
CODE

<?xml version="1.0" encoding="gb2312" ?>
<mmscconf>
   <MMSC_LISTEN_PORT>8801 </MMSC_LISTEN_PORT>
   <CLIENT_LISTEN_PORT>8902 </CLIENT_LISTEN_PORT>

   <MMSC_FROMADDR>"+8613821113111/TYPE=PLMN" </MMSC_FROMADDR>
   <MMSC_IPADDR>"172.16.65.187" </MMSC_IPADDR>
   <MMSC_ROOT>"/" </MMSC_ROOT>
   <MMSC_PORT>9000 </MMSC_PORT>
   <MMSC_LOGIN_NAME>"nan" </MMSC_LOGIN_NAME>
   <MMSC_LOGIN_PWD>"whuang" </MMSC_LOGIN_PWD>
   <MMSC_VASP_ID>"999999" </MMSC_VASP_ID>
   <MMSC_VAS_ID>"9999" </MMSC_VAS_ID>
   <MMS_SUBMIT_REPEATTIME>3 </MMS_SUBMIT_REPEATTIME>
</mmscconf>






不要问我结果 我只研究过程与思路
无双客栈
顶部
无双
荣誉斑竹
Rank: 14 Rank: 14 Rank: 14 Rank: 14
天才猪



UID 4
精华 84
积分 5863
帖子 11390
活跃指数 0
LU金币 4248 个
LU金条 0 个
阅读权限 200
注册 2003-9-16
来自 杭州
#3
 
发表于 2004-3-16 19:30  资料  个人空间  主页 短消息  加为好友 
使用expat时不会帮助检查xml语法 所以你必须保证要分析的xml文件是对的

这点与dom不同 dom会全部分析完 然后生成一颗树的形式
如果语法有错 那就会报错


expat适合小的分析 dom过于庞大 (当然功能也方便 )

如果复杂的树 那expat不能胜任 因为需要定义很多的结构或是变量来保存它的状态
<a name="b" />

<a>
<name>"b"</name>
</a>

这两个在expat中分析的过程是不一样的 你需要保存层的概念 或是分析的中间状态





不要问我结果 我只研究过程与思路
无双客栈
顶部
无双
荣誉斑竹
Rank: 14 Rank: 14 Rank: 14 Rank: 14
天才猪



UID 4
精华 84
积分 5863
帖子 11390
活跃指数 0
LU金币 4248 个
LU金条 0 个
阅读权限 200
注册 2003-9-16
来自 杭州
#4
 
发表于 2004-3-16 19:33  资料  个人空间  主页 短消息  加为好友 
PS
上面的xml文件应该把gb2312修改成utf8
或是iso8859-1

expat默认只支持
UTF-8
UTF-16
ISO-8859-1
US-ASCII


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

不然分析器会报错





不要问我结果 我只研究过程与思路
无双客栈
顶部
sky-walker
LU大天使
Rank: 6 Rank: 6



UID 1585
精华 21
积分 1537
帖子 2989
活跃指数 0
LU金币 2006 个
LU金条 0 个
阅读权限 70
注册 2003-11-3
#5
 
发表于 2004-3-16 21:50  资料  个人空间  短消息  加为好友 
向无双兄努力学习 rose.gif rose.gif

借着人气,支持一下 happy.gif

expat:
http://lfs.linuxsir.org/htdocs/blfscvs/general/expat.html

这里还有一个PHP使用XML expat工具包通过C语言解析XML的例子:
http://www.phpe.net/articles/348.shtml





user posted image
顶部
无双
荣誉斑竹
Rank: 14 Rank: 14 Rank: 14 Rank: 14
天才猪



UID 4
精华 84
积分 5863
帖子 11390
活跃指数 0
LU金币 4248 个
LU金条 0 个
阅读权限 200
注册 2003-9-16
来自 杭州
#6
 
发表于 2004-3-17 12:23  资料  个人空间  主页 短消息  加为好友 
expat移值到很多语言平台上了
如perl php python等

它们的用法基本都是相同的





不要问我结果 我只研究过程与思路
无双客栈
顶部
[广告] 记录自己的思想火花,留住每日的技术积累,尽在拥有属于自己独立域名的博客。
无双
荣誉斑竹
Rank: 14 Rank: 14 Rank: 14 Rank: 14
天才猪



UID 4
精华 84
积分 5863
帖子 11390
活跃指数 0
LU金币 4248 个
LU金条 0 个
阅读权限 200
注册 2003-9-16
来自 杭州
#7
 
发表于 2004-3-17 16:02  资料  个人空间  主页 短消息  加为好友 
中文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:  无双[lizl@yztelecom.com]
*         Company:  www.loveunix.net
*
* =====================================================================================
*/


#include <cstdlib>
#include <fstream>
#include <iostream>
#include <cstring>
#include <string>

#include <windows.h>
#include <expat.h>
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:<%s>/n",el);
   XML_MMSCConfInfo* pmmscinf = (XML_MMSCConfInfo*) data;

   for( int i = 0;attr[i];i+=2 ){
       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文件的结束元素时会调用这个函数
   printf("end element:</%s>/n",el);
   ((XML_MMSCConfInfo*)data)->Element = EM_UNUSE;
   ((XML_MMSCConfInfo*)data)->Depth    --;
}

   static void XMLCALL
parsedata(void *userData,const XML_Char *s,int len)
{
   // 处理数据,注意这里s不是以/0结束的,而是只取len长度
   char buf[1024];
   memset(buf,0,sizeof(buf));
   ConvertFromUTF8(buf,sizeof(buf),s,len);
   printf("data is:[%s]/n",buf);
   SetElementValue(*(XML_MMSCConfInfo*)userData,buf);
}

static int Convert(void *data, const char *s)
{
   //把gb2312转换到ucs2编码,返回对应的ucs2编码值
   //,然后expat会把编码转换到utf-8编码再传给parsedata
   // 因为中文是两个字符,所以下面转换2个字符,在linux上使用iconv
   wchar_t dst[10];
   MultiByteToWideChar(CP_ACP,0,s,2,dst,10);
   return dst[0];
}

static int XMLCALL
XMLEncodingHandle(void *encodingHandlerData,
       const XML_Char *name,
       XML_Encoding *info)
{
   //  map[i]  值意义,
   //      >=0 表示对应的值是单字符
   //      -1  表示非法字符
   //      -2,-3,-4    表示是多字节字符的第一个,分别是2字节、3字节、4字节的字符
   //  使用:
   //      expat会根据map[(unsigned char)s[i]] 的值判断某个字符后面跟多少个字节,然后跳过对应的数目
   //  
   //  info->data    附加数据,这个只会回传给convert和release,expat内部不进行处理,
   //  expat解析过程中会调用convert,解析完后调用release(如果这两个值不为NULL的话)
   //  如果是单字节字符集,那convert可以为NULL,
   //  如果是多字节安符集,convert应该有值,负责把对应的语言转换成ucs2编码

   int i;
   if( !name ||  stricmp(name,"gb2312") )
       return XML_STATUS_ERROR;

   for(i=0;i<128;i++)
       info->map[i] = i;
   for(;i<256;i++)
       info->map[i] = -2;
   info->convert   = Convert;
   info->release   = NULL;
   return 1;//XML_STATUS_OK;
}

/**
* @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;

   //  读文件到缓冲区,expat中可以随时读随时解,不一定要第一次就把文件全部读到内存中去,
   //  可以参考expat自带的例子
   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 ){
       delete[] buf;
       cerr<<"Couldn't allocate memory for parser"<<endl;
       return -1;
   }

   //  如果是utf-8 us-ascii ucs2 iso8859-1 那不用使用下面这个函数,
   //  不然要使用,不使用会报字符集不支持的错误,
   //  读xml头部时会调用到<?xml version="1.0" encoding="gb2312" ?>

   XML_SetUnknownEncodingHandler(parser,XMLEncodingHandle,0);

   //  设置事件处理函数,当碰到开始元素和结可元素时调用哪个函数来处理
   XML_SetElementHandler(parser,xmlstart,xmlend);
   
   //  设置用户数据,如果需要传给处理函数附加的信息,那就使用它,不然就不用
   XML_SetUserData(parser,&Conf);
   
   //  设置当碰到数据时什么处理,
   //  如<img> "hello.gif"</img> 那么碰到img时会调用xmlstart,
   //  读完"hello.gif"后会调用parsedata,碰到/img后会调用xmlend
   XML_SetCharacterDataHandler(parser,parsedata);//添加这个,处理不同字符集的xml文件

   //  解析
   if (XML_Parse(parser, buf, len, done)
           == XML_STATUS_ERROR) {
       cerr<<XML_ErrorString(XML_GetErrorCode(parser))<<" at line "
           <<XML_GetCurrentLineNumber(parser)<<endl;
       err = 1;
   }

   delete[] buf;
   XML_ParserFree(parser);
   return err;
}

static void Serialize(const XML_MMSCConfInfo& Conf)
{

   printf("XML_MMSCConfInfo is:/n"
           "/tDepth:%d/tElement:%d/n"
           "/tMSC_LISTEN_PORT:%d/tCLIENT_LISTEN_PORT:%d/n"
           "/tMMSC_FROMADDR:[%s]  MMSC_IPADDR:[%s]  tMMSC_PORT:%d/n"
           "/tMMSC_ROOT:[%s]/tMSC_LOGIN_NAME:[%s]/tMMSC_LOGIN_PWD:[%s]/n"
           "/tMMSC_VASP_ID:[%s]/tMMSC_VAS_ID:[%s]/tMMS_SUBMIT_REPEATTIME:%d/n",
           Conf.Depth,
           Conf.Element,
           Conf.MMSC_LISTEN_PORT,
           Conf.CLIENT_LISTEN_PORT,
           Conf.MMSC_FROMADDR,
           Conf.MMSC_IPADDR,
           Conf.MMSC_PORT,
           Conf.MMSC_ROOT,
           Conf.MMSC_LOGIN_NAME,
           Conf.MMSC_LOGIN_PWD,
           Conf.MMSC_VASP_ID,
           Conf.MMSC_VAS_ID,
           Conf.MMS_SUBMIT_REPEATTIME);
};

void main()
{
   XML_MMSCConfInfo   Conf;
   ParseMMSCConf(Conf,"conf.xml");    
   Serialize(Conf);
}





不要问我结果 我只研究过程与思路
无双客栈
顶部
[广告] 记录自己的思想火花,留住每日的技术积累,尽在拥有属于自己独立域名的博客。
无双
荣誉斑竹
Rank: 14 Rank: 14 Rank: 14 Rank: 14
天才猪



UID 4
精华 84
积分 5863
帖子 11390
活跃指数 0
LU金币 4248 个
LU金条 0 个
阅读权限 200
注册 2003-9-16
来自 杭州
#8
 
发表于 2004-3-17 16:03  资料  个人空间  主页 短消息  加为好友 
中文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:  无双[lizl@yztelecom.com]
*         Company:  www.loveunix.net
*
* =====================================================================================
*/


#include <cstdlib>
#include <fstream>
#include <iostream>
#include <cstring>
#include <string>

#include <windows.h>
#include <expat.h>
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*2;
       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:<%s>/n",el);
   XML_MMSCConfInfo* pmmscinf = (XML_MMSCConfInfo*) data;

   for( int i = 0;attr[i];i+=2 ){
       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文件的结束元素时会调用这个函数
   printf("end element:</%s>/n",el);
   ((XML_MMSCConfInfo*)data)->Element = EM_UNUSE;
   ((XML_MMSCConfInfo*)data)->Depth    --;
}

   static void XMLCALL
parsedata(void *userData,const XML_Char *s,int len)
{
   // 处理数据,注意这里s不是以/0结束的,而是只取len长度
   char buf[1024];
   memset(buf,0,sizeof(buf));
   ConvertFromUTF8(buf,sizeof(buf),s,len);
   printf("data is:[%s]/n",buf);
   SetElementValue(*(XML_MMSCConfInfo*)userData,buf);
}

static int Convert(void *data, const char *s)
{
   //把gb2312转换到ucs2编码,返回对应的ucs2编码值
   //,然后expat会把编码转换到utf-8编码再传给parsedata
   // 因为中文是两个字符,所以下面转换2个字符,在linux上使用iconv
   wchar_t dst[10];
   MultiByteToWideChar(CP_ACP,0,s,2,dst,10);
   return dst[0];
}

static int XMLCALL
XMLEncodingHandle(void *encodingHandlerData,
       const XML_Char *name,
       XML_Encoding *info)
{
   //  map[i]  值意义,
   //      >=0 表示对应的值是单字符
   //      -1  表示非法字符
   //      -2,-3,-4    表示是多字节字符的第一个,分别是2字节、3字节、4字节的字符
   //  使用:
   //      expat会根据map[(unsigned char)s[i]] 的值判断某个字符后面跟多少个字节,然后跳过对应的数目
   //  
   //  info->data    附加数据,这个只会回传给convert和release,expat内部不进行处理,
   //  expat解析过程中会调用convert,解析完后调用release(如果这两个值不为NULL的话)
   //  如果是单字节字符集,那convert可以为NULL,
   //  如果是多字节安符集,convert应该有值,负责把对应的语言转换成ucs2编码

   int i;
   if( !name ||  stricmp(name,"gb2312") )
       return XML_STATUS_ERROR;

   for(i=0;i<128;i++)
       info->map[i] = i;
   for(;i<256;i++)
       info->map[i] = -2;
   info->convert   = Convert;
   info->release   = NULL;
   return 1;//XML_STATUS_OK;
}

/**
* @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;

   //  读文件到缓冲区,expat中可以随时读随时解,不一定要第一次就把文件全部读到内存中去,
   //  可以参考expat自带的例子
   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 ){
       delete[] buf;
       cerr<<"Couldn't allocate memory for parser"<<endl;
       return -1;
   }

   //  如果是utf-8 us-ascii ucs2 iso8859-1 那不用使用下面这个函数,
   //  不然要使用,不使用会报字符集不支持的错误,
   //  读xml头部时会调用到<?xml version="1.0" encoding="gb2312" ?>

   XML_SetUnknownEncodingHandler(parser,XMLEncodingHandle,0);

   //  设置事件处理函数,当碰到开始元素和结可元素时调用哪个函数来处理
   XML_SetElementHandler(parser,xmlstart,xmlend);
   
   //  设置用户数据,如果需要传给处理函数附加的信息,那就使用它,不然就不用
   XML_SetUserData(parser,&Conf);
   
   //  设置当碰到数据时什么处理,
   //  如<img> "hello.gif"</img> 那么碰到img时会调用xmlstart,
   //  读完"hello.gif"后会调用parsedata,碰到/img后会调用xmlend
   XML_SetCharacterDataHandler(parser,parsedata);//添加这个,处理不同字符集的xml文件

   //  解析
   if (XML_Parse(parser, buf, len, done)
           == XML_STATUS_ERROR) {
       cerr<<XML_ErrorString(XML_GetErrorCode(parser))<<" at line "
           <<XML_GetCurrentLineNumber(parser)<<endl;
       err = 1;
   }

   delete[] buf;
   XML_ParserFree(parser);
   return err;
}

static void Serialize(const XML_MMSCConfInfo& Conf)
{

   printf("XML_MMSCConfInfo is:/n"
           "/tDepth:%d/tElement:%d/n"
           "/tMSC_LISTEN_PORT:%d/tCLIENT_LISTEN_PORT:%d/n"
           "/tMMSC_FROMADDR:[%s]  MMSC_IPADDR:[%s]  tMMSC_PORT:%d/n"
           "/tMMSC_ROOT:[%s]/tMSC_LOGIN_NAME:[%s]/tMMSC_LOGIN_PWD:[%s]/n"
           "/tMMSC_VASP_ID:[%s]/tMMSC_VAS_ID:[%s]/tMMS_SUBMIT_REPEATTIME:%d/n",
           Conf.Depth,
           Conf.Element,
           Conf.MMSC_LISTEN_PORT,
           Conf.CLIENT_LISTEN_PORT,
           Conf.MMSC_FROMADDR,
           Conf.MMSC_IPADDR,
           Conf.MMSC_PORT,
           Conf.MMSC_ROOT,
           Conf.MMSC_LOGIN_NAME,
           Conf.MMSC_LOGIN_PWD,
           Conf.MMSC_VASP_ID,
           Conf.MMSC_VAS_ID,
           Conf.MMS_SUBMIT_REPEATTIME);
};

void main()
{
   XML_MMSCConfInfo   Conf;
   ParseMMSCConf(Conf,"conf.xml");    
   Serialize(Conf);
}





不要问我结果 我只研究过程与思路
无双客栈
顶部
[广告] 记录自己的思想火花,留住每日的技术积累,尽在拥有属于自己独立域名的博客。
无双
荣誉斑竹
Rank: 14 Rank: 14 Rank: 14 Rank: 14
天才猪



UID 4
精华 84
积分 5863
帖子 11390
活跃指数 0
LU金币 4248 个
LU金条 0 个
阅读权限 200
注册 2003-9-16
来自 杭州
#9
 
发表于 2004-3-17 19:29  资料  个人空间  主页 短消息  加为好友 
比较复杂的xml文件需要保存更多的状态
如层深度 每层有什么结点
上一层是什么结点等

这里的例子比较简单 也就没有写那么多





不要问我结果 我只研究过程与思路
无双客栈
顶部
[广告] 记录自己的思想火花,留住每日的技术积累,尽在拥有属于自己独立域名的博客。
无双
荣誉斑竹
Rank: 14 Rank: 14 Rank: 14 Rank: 14
天才猪



UID 4
精华 84
积分 5863
帖子 11390
活跃指数 0
LU金币 4248 个
LU金条 0 个
阅读权限 200
注册 2003-9-16
来自 杭州
#10
 
发表于 2004-3-17 19:40  资料  个人空间  主页 短消息  加为好友 
expat原理
说到expat前先说明sax

sax是Simple API for XML(确实很简单 它除了把<>和数据分开来通知你外其它的什么也没有做 smile.gif )
下面是网上转的 懒的打字那么多 希望那边的炒饭不要跑过来把我当成酸菜送饭 smile.gif
http://www.zdnet.com.cn/developer/code/sto...39036724,00.htm


Megginson采用Java语言开发的,之后SAX很快在Java开发者中流行起来。SAN项目现在负责管理其原始API的开发工作,这是一种公开的、开放源代码软件。不同于其他大多数XML标准的是,SAX没有语言开发商必须遵守的标准SAX参考版本。因此,SAX的不同实现可能采用区别很大的接口。不过,所有的这些实现至少有一个特性是完全一样的,这就是事件驱动。
事件驱动的文档解析

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

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

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

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





不要问我结果 我只研究过程与思路
无双客栈
顶部


当前时区 GMT+8, 现在时间是 2008-11-4 22:17
乐悠LoveUnix论坛-京ICP备05005823号

Thanks to Discuz!  © 2001-2007    Power by LoveUnix.net
Processed in 0.103965 second(s), 8 queries , Gzip enabled
TOP

清除 Cookies - 联系我们 - 乐悠LoveUnix - Archiver
<script src="include/javascript/msn.js" type=text/javascript></script>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值