隱鋒同學(xué)的blog上有關(guān)于libxml2的一篇文章,正好最近要使用這個(gè)庫(kù)來(lái)處理xml文件。
不過(guò)在測(cè)試時(shí)我們發(fā)現(xiàn)用文章里
F. 添加屬性例程代碼 時(shí),添加的keyword結(jié)點(diǎn)后面沒(méi)有回車,
跟后面的結(jié)點(diǎn)擠在一行了,不是很好看。
例如,有以下的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節(jié)點(diǎn)的最后插入一個(gè)keyword的子結(jié)點(diǎn)后的,
該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>
你會(huì)發(fā)現(xiàn)keyword和/filesystem像下面那樣被擠在一起了,這并不是我們想要的.
<keyword1>hello</keyword1><keyword2>hello</keyword2><keyword3>hello</keyword3></filesystem>
通過(guò)設(shè)定 xmlKeepBlanksDefault(0) 以及 xmlSaveFormatFile(...)的format參數(shù)設(shè)置成1,都無(wú)法實(shí)現(xiàn)
在新追加的結(jié)點(diǎn)后面添加回車換行。
在www.xmlsoft.org的官方網(wǎng)站的maillist里關(guān)于這方面的信息非常少。但是,對(duì)我?guī)椭畲筮€是
http://mail.gnome.org/archives/xml/2007-May/msg00043.html 這個(gè)問(wèn)題里的例子代碼,里面在設(shè)置
屬性的時(shí)候用的xmlReadFile函數(shù),而且options參數(shù)設(shè)定的是XML_PARSE_NOBLANKS。
于是,我們用xmlReadFile(...),把它的options參數(shù)設(shè)定成XML_PARSE_NOBLANKS后,就可以自動(dòng)添加
回車了。
那,重新修正了的例子程序是如下那樣,里面只修改了兩條語(yǔ)句。
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文件時(shí)忽略空格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參數(shù)設(shè)定成
XML_PARSE_NOBLANKS;否則的話是不會(huì)在結(jié)點(diǎn)后面添加回車的。
修正2:把
xmlSaveFormatFile的
format參數(shù)修改成1,否則在使用
xmlReadFile打開的xml文件時(shí),在生成的xml文件里是會(huì)把所有的結(jié)點(diǎn)都放到一行里顯示。
另外:
xmlKeepBlanksDefault(0) 除了在讀入xml文件時(shí)忽略空白之外,還會(huì)在寫出xml文件時(shí)在每行前面放置縮進(jìn)(indent)。如果使用
xmlKeepBlanksDefault(1) 則你會(huì)發(fā)現(xiàn)每行前面的縮進(jìn)就沒(méi)有了,但不會(huì)影響回車換行。
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
額外話題: 更新結(jié)點(diǎn)的值得時(shí)候segement fault錯(cuò)誤
下面的代碼是更新XML文件里的某些結(jié)點(diǎn)元素的值的簡(jiǎn)單的例子。
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
}
運(yùn)行該段代碼,有時(shí)候會(huì)在使用libxml2的API函數(shù)xmlNodeSetContent
處發(fā)生段錯(cuò)誤,但不是100%發(fā)生。
只有該結(jié)點(diǎn)在原來(lái)值是某些字符串的時(shí)候會(huì)發(fā)生該錯(cuò)誤,比如說(shuō),
原來(lái)的值是"zo"的時(shí)候就會(huì)讓程序崩潰。
閱讀了libxml2的源代碼發(fā)現(xiàn),xmlNodeSetContent函數(shù),在把結(jié)點(diǎn)值
設(shè)置成新的字符串之前會(huì)調(diào)用xmlFree(cur->content)來(lái)釋放掉原來(lái)
字符串緩沖區(qū)的內(nèi)存。
xmlNodeSetContent函數(shù)的代碼片斷:
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
}
在上面代碼里,如果結(jié)點(diǎn)值得字符串如果在libxml2的字典緩沖區(qū)(cur->doc->dict)里,
就把該字符串釋放掉。而原來(lái)的字符串"zo"恰好在它的字典緩沖里,那這樣傳遞到
xmlFree函數(shù)里的地址是沖區(qū)的一部分而不是緩沖區(qū)的首地址的話,free函數(shù)當(dāng)然
會(huì)死掉了。如果換成其他的字符串就沒(méi)有任何問(wèn)題。
但是,令人不解的是在libxml2的另一部分代碼里,刪除節(jié)點(diǎn)的程序去不是這樣做。
例如,在一個(gè)結(jié)點(diǎn)被刪除后,通常會(huì)使用xmlFreeDoc函數(shù)來(lái)釋放該結(jié)點(diǎn),恰好在這段
代碼里卻是判斷如果該字符串不再字典緩沖區(qū)才去釋放它,也就是調(diào)用宏DICT_FREE
來(lái)完成釋放工作,這兒正好與前面的相反,很難理解為什么會(huì)產(chǎn)生矛盾。
宏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函數(shù)的bug,不過(guò)在maillist和bugzilla也沒(méi)有翻到關(guān)于它
的任何說(shuō)明。
開發(fā)時(shí)間上也不允許去跟libxml2深究它是否是bug,只要采用了迂回策略了。
想辦法不讓libxml2產(chǎn)生字典緩沖不就可以了嗎。
通過(guò)官方手冊(cè)我們可以知道,xmlReadFile函數(shù)可以附加X(jué)ML_PARSE_NODICT選項(xiàng)
來(lái)避免產(chǎn)生字典緩沖。就像下面這樣:
doc
= xmlReadFile(docname, NULL, XML_PARSE_NOBLANK | XML_PARSE_NODICT);
這樣的話,最開始的那段程序運(yùn)行起來(lái)就沒(méi)有任何問(wèn)題了。
隱鋒同學(xué)的文章在這里:
http://www.cnblogs.com/coolattt/articles/804112.html還有,xmlsoft上還有很多使用libxml2的例子程序,可以參考一下:http://xmlsoft.org/examples/index.html