代码改变世界

charactersFound方法中的陷阱

2010-12-17 16:23  乱世文章  阅读(210)  评论(0编辑  收藏  举报

libxml2恐怕是linux下最著名的xml解析库了,其sax API不仅解析效率高,速度快,而且内存占用率更是出奇的低。因此是iphone开发中必不可少的xml解析库。

但sax解析是基于事件驱动的,使用门槛较dom解析为高,不容易为初学者掌握,代码难于阅读和理解。比如笔者写过这样的代码:

 

 

//解析元素体时触发

- (void)charactersFound:(const xmlChar*)ch

len:(int)len

{       

NSString*   string;

string = [[NSString alloc] initWithBytes:ch length:len encoding:NSUTF8StringEncoding];

    // login_status元素体

    switch (flag) {

case 1:

[item setObject:string forKey:@"GNID"];

break;

case 2:

[item setObject:string forKey:@"YWBH"];

break;

case 3:

[item setObject:string forKey:@"QDRQ"];

break;

    }

}

表面上看这段代码没有任何问题,实际上却隐藏着致命的陷阱。
这是因为,当libxml2在解析一个元素体时,会不只一次的回调charactersFound方法!
也就是说一个元素,需要好几次的回调,libxml才会解析出元素体文本。
具体说,标签体文本如果包含多种编码(比如:ipcc日报,同时包含两种字符:asciiutf8编码),那么,libxml2会在解析“ipcc”时调用一次charactersFound方法,然后在“日报”两个字时再次回调charactersFound方法。
因此上面的代码应该做如下修改:
把局部变量string声明为全局的NSMutableString类型。
修改上述方法代码为:

- (void)charactersFound:(const xmlChar*)ch

len:(int)len

{       

[string appendString:[[NSString alloc] initWithBytes:ch length:len encoding:NSUTF8StringEncoding]];

}
然后在元素结束标记回调方法中取得整个string的内容:

//解析元素结束标记时触发

- (void)endElementLocalName:(const xmlChar*)localname

prefix:(const xmlChar*)prefix URI:(const xmlChar*)URI

{

switch (flag) {

case 1:

[item setObject:string forKey:@"GNID"];

break;

case 2:

[item setObject:string forKey:@"YWBH"];

break;

case 3:

[item setObject:string forKey:@"QDRQ"];

break;

}

flag=0;

}

然后在元素开始标记回调方法startElementLocalName中,重置string变量:

[string release];

    string=[[NSMutableString alloc]init];