隐锋同学 的blog上有关于libxml2的一篇文章,正好最近要使用这个库来处理xml文件。 不过在测试时我们发现用文章里F. 添加属性例程代码 时,添加的keyword结点后面没有回车, 跟后面的结点挤在一行了,不是很好看。 例如,有以下的xml例子文件
1
<?
xml version="1.0"
?>
2
<
BODY
>
3
<
filesystem
>
4
<
filesystemKeyData
>
5
<
filesystemName
>
Ext3
</
filesystemName
>
6
<
versionNumber
>
123
</
versionNumber
>
7
<
option
>
good
</
option
>
8
</
filesystemKeyData
>
9
<
timestampSec
>
456
</
timestampSec
>
10
<
status
>
heasjdkfjaskdfjsk
</
status
>
11
</
filesystem
>
12
<
filesystem
>
13
<
filesystemKeyData
>
14
<
filesystemName
>
Ext3
</
filesystemName
>
15
<
versionNumber
>
123
</
versionNumber
>
16
<
option
>
good
</
option
>
17
</
filesystemKeyData
>
18
<
timestampSec
>
456
</
timestampSec
>
19
<
status
>
heasjdkfjaskdfjsk
</
status
>
20
</
filesystem
>
21
</
BODY
>
例如,使用该文章例子中的代码在上面的filesystem节点的最后插入一个keyword的子结点后的, 该xml文件的表示如下:
1
<?
xml version="1.0"
?>
2
<
BODY
>
3
<
filesystem
>
4
<
filesystemKeyData
>
5
<
filesystemName
>
Ext3
</
filesystemName
>
6
<
versionNumber
>
123
</
versionNumber
>
7
<
option
>
good
</
option
>
8
</
filesystemKeyData
>
9
<
timestampSec
>
456
</
timestampSec
>
10
<
status
>
heasjdkfjaskdfjsk
</
status
>
11
<
keyword1
>
hello
</
keyword1
><
keyword2
>
hello
</
keyword2
><
keyword3
>
hello
</
keyword3
></
filesystem
>
12
<
filesystem
>
13
<
filesystemKeyData
>
14
<
filesystemName
>
Ext3
</
filesystemName
>
15
<
versionNumber
>
123
</
versionNumber
>
16
<
option
>
good
</
option
>
17
</
filesystemKeyData
>
18
<
timestampSec
>
456
</
timestampSec
>
19
<
status
>
heasjdkfjaskdfjsk
</
status
>
20
<
keyword1
>
hello
</
keyword1
><
keyword2
>
hello
</
keyword2
><
keyword3
>
hello
</
keyword3
></
filesystem
>
21
</
BODY
>
你会发现keyword和/filesystem像下面那样被挤在一起了,这并不是我们想要的.< keyword1 > hello </ keyword1 >< keyword2 > hello </ keyword2 >< keyword3 > hello </ keyword3 ></ filesystem > 通过设定 xmlKeepBlanksDefault(0) 以及 xmlSaveFormatFile(...)的format参数设置成1,都无法实现 在新追加的结点后面添加回车换行。 在www.xmlsoft.org 的官方网站的maillist里关于这方面的信息非常少。但是,对我帮助最大还是http://mail.gnome.org/archives/xml/2007-May/msg00043.html 这个问题里的例子代码,里面在设置 属性的时候用的xmlReadFile函数,而且options参数设定的是XML_PARSE_NOBLANKS。 于是,我们用xmlReadFile(...),把它的options参数设定成XML_PARSE_NOBLANKS后,就可以自动添加 回车了。 那,重新修正了的例子程序是如下那样,里面只修改了两条语句。
1
#include
<
stdio.h
>
2
#include
<
string
.h
>
3
#include
<
stdlib.h
>
4
#include
<
libxml
/
xmlmemory.h
>
5
#include
<
libxml
/
parser.h
>
6
void
7
parseStory (xmlDocPtr doc, xmlNodePtr cur,
char
*
keyword)
8
{ 9 xmlNewTextChild (cur, NULL, " keyword1 " , keyword); 10 xmlNewTextChild (cur, NULL, " keyword2 " , keyword); 11 xmlNewTextChild (cur, NULL, " keyword3 " , keyword); 12 return ; 13 }
14
15
xmlDocPtr
16
parseDoc (
char
*
docname,
char
*
keyword)
17
{ 18 xmlDocPtr doc;19 xmlNodePtr cur;20 // doc = xmlParseFile (docname); 21 doc = xmlReadFile(docname, NULL, XML_PARSE_NOBLANKS);
// 读取xml文件时忽略空格
22 if (doc == NULL) 23 { 24 fprintf (stderr, " Document not parsed successfully. \n " ); 25 return (NULL); 26 }27 cur = xmlDocGetRootElement (doc); 28 if (cur == NULL) 29 { 30 fprintf (stderr, " empty document\n " ); 31 xmlFreeDoc (doc);32 return (NULL); 33 }34 if (xmlStrcmp (cur -> name, ( const xmlChar * ) " BODY " )) 35 { 36 fprintf (stderr, " document of the wrong type, root node != story\n " ); 37 xmlFreeDoc (doc);38 return (NULL); 39 }40 cur = cur -> xmlChildrenNode; 41 while (cur != NULL) 42 { 43 if (( ! xmlStrcmp (cur -> name, ( const xmlChar * ) " filesystem " ))) 44 { 45 parseStory (doc, cur, keyword);46 }47 cur = cur -> next; 48 }49 return (doc); 50 }
51
52
int
53
main (
int
argc,
char
**
argv)
54
{ 55 char * docname; 56 char * keyword; 57 xmlDocPtr doc;58 if (argc <= 2 ) 59 { 60 printf (" Usage: %s docname, keyword\n " , argv[ 0 ]); 61 return ( 0 ); 62 }63 docname = argv[ 1 ]; 64 keyword = argv[ 2 ]; 65 doc = parseDoc (docname, keyword); 66 if (doc != NULL) 67 { 68 // xmlSaveFormatFile (docname, doc, 0); 69 xmlSaveFormatFile (docname, doc, 1 ); 70 xmlFreeDoc (doc);71 }72 return ( 1 ); 73 }
74
修正1:是把xmlParseFile替换成xmlReadFile ,并且是options 参数设定成XML_PARSE_NOBLANKS ;否则的话是不会在结点后面添加回车的。 修正2:把xmlSaveFormatFile 的format 参数修改成1,否则在使用xmlReadFile 打开的xml文件时,在生成的xml文件里是会把所有的结点都放到一行里显示。 另外:xmlKeepBlanksDefault(0 ) 除了在读入xml文件时忽略空白之外,还会在写出xml文件时在每行前面放置缩进(indent)。如果使用xmlKeepBlanksDefault(1) 则你会发现每行前面的缩进就没有了,但不会影响回车换行。 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////额外话题: 更新结点的值得时候segement fault错误 下面的代码是更新XML文件里的某些结点元素的值的简单的例子。
1
xmlNodePtr element;
2
//
3
xmlNodePtr childrenNodePtr
=
element
->
children;
4
while
(childrenNodePtr
!=
NULL)
5
{ 6 if (childrenNodePtr -> type == XML_TEXT_NODE) 7 { 8 xmlNodeSetContent(childrenNodePtr, (const xmlChar * )"world"); 9 return NORMAL_RET; 10 }11 childrenNodePtr = childrenNodePtr -> next; 12 }
运行该段代码,有时候会在使用libxml2的API函数xmlNodeSetContent 处发生段错误,但不是100%发生。 只有该结点在原来值是某些字符串的时候会发生该错误,比如说, 原来的值是"zo"的时候就会让程序崩溃。 阅读了libxml2的源代码发现,xmlNodeSetContent函数,在把结点值 设置成新的字符串之前会调用xmlFree(cur->content)来释放掉原来 字符串缓冲区的内存。 xmlNodeSetContent函数的代码片断:
1
switch
(cur
->
type)
{ 2 case XML_DOCUMENT_FRAG_NODE: 3 case XML_ELEMENT_NODE: 4 case XML_ATTRIBUTE_NODE: 5 if (cur -> children != NULL) xmlFreeNodeList(cur -> children); 6 cur-> children = xmlStringGetNodeList(cur -> doc, content); 7 UPDATE_LAST_CHILD_AND_PARENT(cur) 8 break ; 9 case XML_TEXT_NODE: 10 case XML_CDATA_SECTION_NODE: 11 case XML_ENTITY_REF_NODE: 12 case XML_ENTITY_NODE: 13 case XML_PI_NODE: 14 case XML_COMMENT_NODE: 15 if ((cur -> content != NULL) && 16 (cur-> content != (xmlChar * ) & (cur -> properties))) { 17 if (!((cur->doc != NULL) && (cur->doc->dict != NULL) && 18 (xmlDictOwns(cur->doc->dict, cur->content)))) 19 xmlFree(cur->content); 20 }
在上面代码里,如果结点值得字符串如果在libxml2的字典缓冲区(cur->doc->dict)里, 就把该字符串释放掉。而原来的字符串"zo"恰好在它的字典缓冲里,那这样传递到 xmlFree函数里的地址是冲区的一部分而不是缓冲区的首地址的话,free函数当然 会死掉了。如果换成其他的字符串就没有任何问题。 但是,令人不解的是在libxml2的另一部分代码里,删除节点的程序去不是这样做。 例如,在一个结点被删除后,通常会使用xmlFreeDoc函数来释放该结点,恰好在这段 代码里却是判断如果该字符串不再字典缓冲区才去释放它,也就是调用宏DICT_FREE 来完成释放工作,这儿正好与前面的相反,很难理解为什么会产生矛盾。 宏DICT_FREE的代码:
1
/**//**
2
* DICT_FREE:
3
* @str: a string
4
*
5
* Free a string if it is not owned by the "dict" dictionnary in the
6
* current scope
7
*/
8
#define DICT_FREE(str) \
9
if ((str) && ((!dict) || \
10
(xmlDictOwns(dict, (const xmlChar *)(str)) == 0 ))) \
11
xmlFree((char *)(str));
上面这段代码判断是该字符串如果不在字典缓冲里才去释放。 在考虑这是否是xmlNodeSetContent函数的bug,不过在maillist和bugzilla也没有翻到关于它 的任何说明。 开发时间上也不允许去跟libxml2深究它是否是bug,只要采用了迂回策略了。 想办法不让libxml2产生字典缓冲不就可以了吗。 通过官方手册我们可以知道,xmlReadFile函数可以附加XML_PARSE_NODICT选项 来避免产生字典缓冲。就像下面这样: doc = xmlReadFile(docname, NULL, XML_PARSE_NOBLANK | XML_PARSE_NODICT ); 这样的话,最开始的那段程序运行起来就没有任何问题了。 隐锋同学的文章在这里:http://www.cnblogs.com/coolattt/articles/804112.html
转载于:https://blog.51cto.com/laokaddk/593043