這里主要講述libxml2在linux下的使用。
(以下內容除了linux下的安裝步驟是自己寫(xiě)的,其余均出自
http://www.blogjava.net/wxb_nudt/archive/2007/11/28/161340.html
,由于該篇文章講解的很周詳,我也沒(méi)必要重寫(xiě)一遍了。假如該篇文章早出些時(shí)候,早期研發(fā)我也不會(huì )走很多彎路了。雖然在該篇文章出現之前我已對libxml2較熟悉了并研發(fā)完畢,但為了備忘,現在將該篇文章作為收藏之用。在此再次感謝這篇文章的大俠為大家整理了這篇文章^_^)
1. 下載和安裝LIBXML2
Libxml2是個(gè)C語(yǔ)言的XML程式庫,能夠簡(jiǎn)單方便的提供對XML文檔的各種操作,并且支持XPATH查詢(xún),連同部分的支持XSLT轉換等功能。Libxml2的下載地址是
http://xmlsoft.org/
,完全版的庫是開(kāi)源的,并且帶有例子程式和說(shuō)明文檔。最好將這個(gè)庫先下載下來(lái),因為這樣能夠查看其中的文檔和例子。
由于我是在linux下用C語(yǔ)言進(jìn)行研發(fā)的,所以我下載的是libxml2-2.6.20.tar.gz版本的源碼包。
具體安裝步驟:
1、解壓:$tar zxvf libxml2-2.6.20.tar.gz
2、進(jìn)入解壓后的安裝目錄:$cd libxml2-2.6.20
3、安裝三部曲:1)$./configure
2)$make
3)$make install
安裝完畢。
2. Libxml2中的數據類(lèi)型和函數
一個(gè)函數庫中可能有幾百種數據類(lèi)型連同幾千個(gè)函數,但是記住大師的話(huà),90%的功能都是由30%的內容提供的。對于libxml2,我認為搞懂以下的數據類(lèi)型和函數就足夠了。
2.1 內部字符類(lèi)型xmlChar
xmlChar是Libxml2中的字符類(lèi)型,庫中任何字符、字符串都是基于這個(gè)數據類(lèi)型。事實(shí)上他的定義是:xmlstring.h
typedef unsigned char xmlChar;
使用unsigned char作為內部字符格式是考慮到他能很好適應UTF-8編碼,而UTF-8編碼正是libxml2的內部編碼,其他格式的編碼要轉換為這個(gè)編碼才能在libxml2中使用。
還經(jīng)常能夠看到使用xmlChar*作為字符串類(lèi)型,很多函數會(huì )返回一個(gè)動(dòng)態(tài)分配內存的xmlChar*變量,使用這樣的函數時(shí)記得要手動(dòng)刪除內存。
2.2 xmlChar相關(guān)函數
如同標準c中的char類(lèi)型相同,xmlChar也有動(dòng)態(tài)內存分配、字符串操作等相關(guān)函數。例如xmlMalloc是動(dòng)態(tài)分配內存的函數;xmlFree是配套的釋放內存函數;xmlStrcmp是字符串比較函數等等。
基本上x(chóng)mlChar字符串相關(guān)函數都在xmlstring.h中定義;而動(dòng)態(tài)內存分配函數在xmlmemory.h中定義。
2.3 xmlChar*和其他類(lèi)型之間的轉換
另外要注意,因為總是要在xmlChar*和char*之間進(jìn)行類(lèi)型轉換,所以定義了一個(gè)宏BAD_CAST,其定義如下:xmlstring.h
#define BAD_CAST (xmlChar *)
原則上來(lái)說(shuō),unsigned char和char之間進(jìn)行強制類(lèi)型轉換是沒(méi)有問(wèn)題的。
2.4 文檔類(lèi)型xmlDoc、指針xmlDocPtr
xmlDoc是個(gè)struct,保存了一個(gè)xml的相關(guān)信息,例如文檔名、文檔類(lèi)型、子節點(diǎn)等等;xmlDocPtr等于xmlDoc*,他搞成這個(gè)樣子總讓人以為是智能指針,其實(shí)不是,要手動(dòng)刪除的。
xmlNewDoc函數創(chuàng )建一個(gè)新的文檔指針。
xmlParseFile函數以默認方式讀入一個(gè)UTF-8格式的文檔,并返回文檔指針。
xmlReadFile函數讀入一個(gè)帶有某種編碼的xml文檔,并返回文檔指針;細節見(jiàn)libxml2參考手冊。
xmlFreeDoc釋放文檔指針。特別注意,當您調用xmlFreeDoc時(shí),該文檔任何包含的節點(diǎn)內存都被釋放,所以一般來(lái)說(shuō)無(wú)需手動(dòng)調用xmlFreeNode或xmlFreeNodeList來(lái)釋放動(dòng)態(tài)分配的節點(diǎn)內存,除非您把該節點(diǎn)從文檔中移除了。一般來(lái)說(shuō),一個(gè)文檔中任何節點(diǎn)都應該動(dòng)態(tài)分配,然后加入文檔,最后調用xmlFreeDoc一次釋放任何節點(diǎn)申請的動(dòng)態(tài)內存,這也是為什么我們很少看見(jiàn)xmlNodeFree的原因。
xmlSaveFile將文檔以默認方式存入一個(gè)文檔。
xmlSaveFormatFileEnc可將文檔以某種編碼/格式存入一個(gè)文檔中。
2.5 節點(diǎn)類(lèi)型xmlNode、指針xmlNodePtr
節點(diǎn)應該是xml中最重要的元素了,xmlNode代表了xml文檔中的一個(gè)節點(diǎn),實(shí)現為一個(gè)struct,內容很豐富:tree.h
typedef struct _xmlNode xmlNode;
typedef xmlNode *xmlNodePtr;
struct _xmlNode {
void *_private;/* application data */
xmlElementType type; /* type number, must be second ! */
const xmlChar *name; /* the name of the node, or the entity */
struct _xmlNode *children; /* parent->childs link */
struct _xmlNode *last; /* last child link */
struct _xmlNode *parent;/* child->parent link */
struct _xmlNode *next; /* next sibling link */
struct _xmlNode *prev; /* previous sibling link */
struct _xmlDoc *doc;/* the containing document */
/* End of common part */
xmlNs *ns; /* pointer to the associated namespace */
xmlChar *content; /* the content */
struct _xmlAttr *properties;/* properties list */
xmlNs *nsDef; /* namespace definitions on this node */
void *psvi;/* for type/PSVI informations */
unsigned short line; /* line number */
unsigned short extra; /* extra data for XPath/XSLT */
};
能夠看到,節點(diǎn)之間是以鏈表和樹(shù)兩種方式同時(shí)組織起來(lái)的,next和prev指針能夠組成鏈表,而parent和children能夠組織為樹(shù)。同時(shí)更有以下重要元素:
l 節點(diǎn)中的文字內容:content;
l 節點(diǎn)所屬文檔:doc;
l 節點(diǎn)名字:name;
l 節點(diǎn)的namespace:ns;
l 節點(diǎn)屬性列表:properties;
Xml文檔的操作其根本原理就是在節點(diǎn)之間移動(dòng)、查詢(xún)節點(diǎn)的各項信息,并進(jìn)行增加、刪除、修改的操作。
xmlDocSetRootElement函數能夠將一個(gè)節點(diǎn)配置為某個(gè)文檔的根節點(diǎn),這是將文檔和節點(diǎn)連接起來(lái)的重要手段,當有了根結點(diǎn)以后,任何子節點(diǎn)就能夠依次連接上根節點(diǎn),從而組織成為一個(gè)xml樹(shù)。
2.6 節點(diǎn)集合類(lèi)型xmlNodeSet、指針xmlNodeSetPtr
節點(diǎn)集合代表一個(gè)由節點(diǎn)組成的變量,節點(diǎn)集合只作為Xpath的查詢(xún)結果而出現(XPATH的介紹見(jiàn)后面),因此被定義在xpath.h中,其定義如下:
/*
* A node-set (an unordered collection of nodes without duplicates).
*/
typedef struct _xmlNodeSet xmlNodeSet;
typedef xmlNodeSet *xmlNodeSetPtr;
struct _xmlNodeSet {
int nodeNr; /* number of nodes in the set */
int nodeMax; /* size of the array as allocated */
xmlNodePtr *nodeTab;/* array of nodes in no particular order */
/* @@ with_ns to check wether namespace nodes should be looked at @@ */
};
能夠看出,節點(diǎn)集合有三個(gè)成員,分別是節點(diǎn)集合的節點(diǎn)數、最大可容納的節點(diǎn)數,連同節點(diǎn)數組頭指針。對節點(diǎn)集合中各個(gè)節點(diǎn)的訪(fǎng)問(wèn)方式很簡(jiǎn)單,如下:
xmlNodeSetPtr nodeset = XPATH查詢(xún)結果;
for (int i = 0; i nodeNr; i++)
{
nodeset->nodeTab;
}
注意,libxml2是個(gè)c函數庫,因此其函數和數據類(lèi)型都使用c語(yǔ)言的方式來(lái)處理。假如是c++,我想我寧愿用STL中的vector來(lái)表示一個(gè)節點(diǎn)集合更好,而且沒(méi)有內存泄漏或溢出的擔憂(yōu)。
3. 簡(jiǎn)單xml操作例子
了解以上基本知識之后,就能夠進(jìn)行一些簡(jiǎn)單的xml操作了。當然,還沒(méi)有涉及到內碼轉換(使得xml中能夠處理中文)、xpath等較復雜的操作。
3.1 創(chuàng )建xml文檔
有了上面的基礎,創(chuàng )建一個(gè)xml文檔顯得很簡(jiǎn)單,其流程如下:
l 用xmlNewDoc函數創(chuàng )建一個(gè)文檔指針doc;
l 用xmlNewNode函數創(chuàng )建一個(gè)節點(diǎn)指針root_node;
l 用xmlDocSetRootElement將root_node配置為doc的根結點(diǎn);
l 給root_node添加一系列的子節點(diǎn),并配置子節點(diǎn)的內容和屬性;
l 用xmlSaveFile將xml文檔存入文檔;
l 用xmlFreeDoc函數關(guān)閉文檔指針,并清除本文檔中任何節點(diǎn)動(dòng)態(tài)申請的內存。
注意,有多種方式能夠添加子節點(diǎn):第一是用xmlNewTextChild直接添加一個(gè)文本子節點(diǎn);第二是先創(chuàng )建新節點(diǎn),然后用xmlAddChild將新節點(diǎn)加入上層節點(diǎn)。
源代碼文檔是CreateXmlFile.cpp,如下:
/********************************************************************
created: 2007/11/09
created: 9:11:2007 15:34
filename: CreateXmlFile.cpp
author: Wang xuebin
depend: libxml2.lib
build: nmake TARGET_NAME=CreateXmlFile
purpose: 創(chuàng )建一個(gè)xml文檔
*********************************************************************/
#include
#include
#include
#include
int main()
{
//定義文檔和節點(diǎn)指針
xmlDocPtr doc = xmlNewDoc(BAD_CAST"1.0");
xmlNodePtr root_node = xmlNewNode(NULL,BAD_CAST"root");
//配置根節點(diǎn)
xmlDocSetRootElement(doc,root_node);
//在根節點(diǎn)中直接創(chuàng )建節點(diǎn)
xmlNewTextChild(root_node, NULL, BAD_CAST "newNode1", BAD_CAST "newNode1 content");
xmlNewTextChild(root_node, NULL, BAD_CAST "newNode2", BAD_CAST "newNode2 content");
xmlNewTextChild(root_node, NULL, BAD_CAST "newNode3", BAD_CAST "newNode3 content");
//創(chuàng )建一個(gè)節點(diǎn),配置其內容和屬性,然后加入根結點(diǎn)
xmlNodePtr node = xmlNewNode(NULL,BAD_CAST"node2");
xmlNodePtr content = xmlNewText(BAD_CAST"NODE CONTENT");
xmlAddChild(root_node,node);
xmlAddChild(node,content);
xmlNewProp(node,BAD_CAST"attribute",BAD_CAST "yes");
//創(chuàng )建一個(gè)兒子和孫子節點(diǎn)
node = xmlNewNode(NULL, BAD_CAST "son");
xmlAddChild(root_node,node);
xmlNodePtr grandson = xmlNewNode(NULL, BAD_CAST "grandson");
xmlAddChild(node,grandson);
xmlAddChild(grandson, xmlNewText(BAD_CAST "This is a grandson node"));
//存儲xml文檔
int nRel = xmlSaveFile("CreatedXml.xml",doc);
if (nRel != -1)
{
cout一個(gè)xml文檔被創(chuàng )建,寫(xiě)入"個(gè)字節" }
//釋放文檔內節點(diǎn)動(dòng)態(tài)申請的內存
xmlFreeDoc(doc);
return 1;
}
編譯鏈接命令如下:
nmake TARGET_NAME=CreateXmlFile
然后執行可執行文檔CreateXmlFile.exe,會(huì )生成一個(gè)xml文檔CreatedXml.xml,打開(kāi)后如下所示:
root>
newNode1>newNode1 contentnewNode1>
newNode2>newNode2 contentnewNode2>
newNode3>newNode3 contentnewNode3>
node2 attribute="yes">NODE CONTENTnode2>
son>
grandson>This is a grandson nodegrandson>
son>
root>
最好使用類(lèi)似XMLSPY這樣的工具打開(kāi),因為這些工具能夠自動(dòng)整理xml文檔的柵格,否則很有可能是沒(méi)有任何換行的一個(gè)xml文檔,可讀性較差。
3.2 解析xml文檔
解析一個(gè)xml文檔,從中取出想要的信息,例如節點(diǎn)中包含的文字,或某個(gè)節點(diǎn)的屬性,其流程如下:
l 用xmlReadFile函數讀出一個(gè)文檔指針doc;
l 用xmlDocGetRootElement函數得到根節點(diǎn)curNode;
l curNode->xmlChildrenNode就是根節點(diǎn)的子節點(diǎn)集合;
l 輪詢(xún)子節點(diǎn)集合,找到所需的節點(diǎn),用xmlNodeGetContent取出其內容;
l 用xmlHasProp查找含有某個(gè)屬性的節點(diǎn);
l 取出該節點(diǎn)的屬性集合,用xmlGetProp取出其屬性值;
l 用xmlFreeDoc函數關(guān)閉文檔指針,并清除本文檔中任何節點(diǎn)動(dòng)態(tài)申請的內存。
注意:節點(diǎn)列表的指針依然是xmlNodePtr,屬性列表的指針也是xmlAttrPtr,并沒(méi)有xmlNodeList或xmlAttrList這樣的類(lèi)型??醋髁斜淼臅r(shí)候使用他們的next和prev鏈表指針來(lái)進(jìn)行輪詢(xún)。只有在Xpath中有xmlNodeSet這種類(lèi)型,其使用方法前面已介紹了。
源代碼如下:ParseXmlFile.cpp
/********************************************************************
created: 2007/11/15
created: 15:11:2007 11:47
filename: ParseXmlFile.cpp
author: Wang xuebin
depend: libxml2.lib
build: nmake TARGET_NAME=ParseXmlFile
purpose: 解析xml文檔
*********************************************************************/
#include
#include
int main(int argc, char* argv[])
{
xmlDocPtr doc; //定義解析文檔指針
xmlNodePtr curNode; //定義結點(diǎn)指針(您需要他為了在各個(gè)結點(diǎn)間移動(dòng))
xmlChar *szKey; //臨時(shí)字符串變量
char *szDocName;
if (argc {
printf("Usage: %s docname"n", argv[0]);
return(0);
}
szDocName = argv[1];
doc = xmlReadFile(szDocName,"GB2312",XML_PARSE_RECOVER); //解析文檔
//檢查解析文檔是否成功,假如不成功,libxml將指一個(gè)注冊的錯誤并停止。
//一個(gè)常見(jiàn)錯誤是不適當的編碼。XML標準文檔除了用UTF-8或UTF-16外還可用其他編碼保存。
//假如文檔是這樣,libxml將自動(dòng)地為您轉換到UTF-8。更多關(guān)于XML編碼信息包含在XML標準中.
if (NULL == doc)
{
fprintf(stderr,"Document not parsed successfully. "n");
return -1;
}
curNode = xmlDocGetRootElement(doc); //確定文檔根元素
/*檢查確認當前文檔中包含內容*/
if (NULL == curNode)
{
fprintf(stderr,"empty document"n");
xmlFreeDoc(doc);
return -1;
}
/*在這個(gè)例子中,我們需要確認文檔是正確的類(lèi)型?!皉oot”是在這個(gè)示例中使用文檔的根類(lèi)型。*/
if (xmlStrcmp(curNode->name, BAD_CAST "root"))
{
fprintf(stderr,"document of the wrong type, root node != root");
xmlFreeDoc(doc);
return -1;
}
curNode = curNode->xmlChildrenNode;
xmlNodePtr propNodePtr = curNode;
while(curNode != NULL)
{
//取出節點(diǎn)中的內容
if ((!xmlStrcmp(curNode->name, (const xmlChar *)"newNode1")))
{
szKey = xmlNodeGetContent(curNode);
printf("newNode1: %s"n", szKey);
xmlFree(szKey);
}
//查找帶有屬性attribute的節點(diǎn)
if (xmlHasProp(curNode,BAD_CAST "attribute"))
{
propNodePtr = curNode;
}
curNode = curNode->next;
}
//查找屬性
xmlAttrPtr attrPtr = propNodePtr->properties;
while (attrPtr != NULL)
{
if (!xmlStrcmp(attrPtr->name, BAD_CAST "attribute"))
{
xmlChar* szAttr = xmlGetProp(propNodePtr,BAD_CAST "attribute");
cout xmlFree(szAttr);
}
attrPtr = attrPtr->next;
}
xmlFreeDoc(doc);
return 0;
}
編譯鏈接命令如下:
nmake TARGET_NAME=ParseXmlFile
執行命令如下,使用第一次創(chuàng )建的xml文檔作為輸入:
ParseXmlFile.exe CreatedXml.xml
觀(guān)察源代碼可發(fā)現,任何以查詢(xún)方式得到的xmlChar*字符串都必須使用xmlFree函數手動(dòng)釋放。否則會(huì )造成內存泄漏。
3.3 修改xml文檔
有了上面的基礎,修改xml文檔的內容就很簡(jiǎn)單了。首先打開(kāi)一個(gè)已存在的xml文檔,順著(zhù)根結點(diǎn)找到需要添加、刪除、修改的地方,調用相應的xml函數對節點(diǎn)進(jìn)行增、刪、改操作。源代碼見(jiàn)ChangeXmlFile,編譯鏈接方法如上。執行下面的命令:
ChangeXmlFile.exe CreatedXml.xml
能夠得到一個(gè)修改后的xml文檔ChangedXml.xml,如下:
root>
newNode2>content changednewNode2>
newNode3 newAttr="YES">newNode3 contentnewNode3>
node2 attribute="no">NODE CONTENTnode2>
son>
grandson>This is a grandson nodegrandson>
newGrandSon>new contentnewGrandSon>
son>
root>
需要注意的是,并沒(méi)有xmlDelNode或xmlRemoveNode函數,我們刪除節點(diǎn)使用的是以下一段代碼:
if (!xmlStrcmp(curNode->name, BAD_CAST "newNode1"))
{
xmlNodePtr tempNode;
tempNode = curNode->next;
xmlUnlinkNode(curNode);
xmlFreeNode(curNode);
curNode = tempNode;
continue;
}
即將當前節點(diǎn)從文檔中斷鏈(unlink),這樣本文檔就不會(huì )再包含這個(gè)子節點(diǎn)。這樣做需要使用一個(gè)臨時(shí)變量來(lái)存儲斷鏈節點(diǎn)的后續節點(diǎn),并記得要手動(dòng)刪除斷鏈節點(diǎn)的內存。
3.4 使用XPATH查找xml文檔
簡(jiǎn)而言之,XPATH之于xml,好比SQL之于關(guān)系數據庫。要在一個(gè)復雜的xml文檔中查找所需的信息,XPATH簡(jiǎn)直是必不可少的工具。XPATH語(yǔ)法簡(jiǎn)單易學(xué),并且有一個(gè)很好的官方教程,見(jiàn)
http://www.zvon.org/xxl/XPathTutorial/Output_chi/introduction.html
。這個(gè)站點(diǎn)的XML各種教程齊全,并且有包括中文在內的各國語(yǔ)言版本,真是讓我喜歡到很!
使用XPATH之前,必須首先熟悉幾個(gè)數據類(lèi)型和函數,他們是使用XPATH的前提。在libxml2中使用Xpath是很簡(jiǎn)單的,其流程如下:
l 定義一個(gè)XPATH上下文指針xmlXPathContextPtr context,并且使用xmlXPathNewContext函數來(lái)初始化這個(gè)指針;
l 定義一個(gè)XPATH對象指針xmlXPathObjectPtr result,并且使用xmlXPathEvalExpression函數來(lái)計算Xpath表達式,得到查詢(xún)結果,將結果存入對象指針中;
l 使用result->nodesetval得到節點(diǎn)集合指針,其中包含了任何符合Xpath查詢(xún)結果的節點(diǎn);
l 使用xmlXPathFreeContext釋放上下文指針;
l 使用xmlXPathFreeObject釋放Xpath對象指針;
具體的使用方法能夠看XpathForXmlFile.cpp的這一段代碼,其功能是查找符合某個(gè)Xpath語(yǔ)句的對象指針:
xmlXPathObjectPtr getNodeSet(xmlDocPtr doc, const xmlChar *szXpath)
{
xmlXPathContextPtr context; //XPATH上下文指針
xmlXPathObjectPtr result; //XPATH對象指針,用來(lái)存儲查詢(xún)結果
context = xmlXPathNewContext(doc); //創(chuàng )建一個(gè)XPath上下文指針
if (context == NULL)
{
printf("context is NULL"n");
return NULL;
}
result = xmlXPathEvalExpression(szXpath, context); //查詢(xún)XPath表達式,得到一個(gè)查詢(xún)結果
xmlXPathFreeContext(context); //釋放上下文指針
if (result == NULL)
{
printf("xmlXPathEvalExpression return NULL"n");
return NULL;
}
if (xmlXPathNodeSetIsEmpty(result->nodesetval)) //檢查查詢(xún)結果是否為空
{
xmlXPathFreeObject(result);
printf("nodeset is empty"n");
return NULL;
}
return result;
}
一個(gè)完整的使用Xpath的例子在代碼XpathForXmlFile.cpp中,他查找一個(gè)xml文檔中符合"/root/node2[@attribute=‘yes‘]"語(yǔ)句的結果,并且將找到的節點(diǎn)的屬性和內容打印出來(lái)。編譯鏈接命令如下:
nmake TARGET_NAME=XpathForXmlFile
執行方式如下:
XpathForXmlFile.exe CreatedXml.xml
觀(guān)察結果能夠看出找到了一個(gè)節點(diǎn),即root下面node2節點(diǎn),他的attribute屬性值正好等于yes。更多關(guān)于Xpath的內容能夠參考XPATH官方手冊。只有掌控了XPATH,才掌控了使用大型XML文檔的方法,否則每尋找一個(gè)節點(diǎn)都要從根節點(diǎn)找起,會(huì )把人累死。
4. 用ICONV解決XML中的中文問(wèn)題
Libxml2中默認的內碼是UTF-8,任何使用libxml2進(jìn)行處理的xml文檔,必須首先顯式或默認的轉換為UTF-8編碼才能被處理。
要在xml中使用中文,就必須能夠在UTF-8和GB2312內碼(較常用的一種簡(jiǎn)體中文編碼)之間進(jìn)行轉換。Libxml2提供了默認的內碼轉換機制,并且在libxml2的Tutorial中有一個(gè)例子,事實(shí)證實(shí)這個(gè)例子并不適合用來(lái)轉換中文。
所以需要我們顯式的使用ICONV來(lái)進(jìn)行內碼轉換,libxml2本身也是使用ICONV進(jìn)行轉換的。ICONV是個(gè)專(zhuān)門(mén)用來(lái)進(jìn)行編碼轉換的庫,基本上支持現在任何常用的編碼。他是glibc庫的一個(gè)部分,常常被用于UNIX系統中。當然,在windows下面使用也沒(méi)有任何問(wèn)題。前面已提到了ICONV的安裝和使用方法,這里主要講一下編程相關(guān)問(wèn)題。
本節其實(shí)和xml連同libxml2沒(méi)有太大關(guān)系,您能夠把他簡(jiǎn)單看作是個(gè)編碼轉換方面的專(zhuān)題。我們僅僅需要學(xué)會(huì )使用兩個(gè)函數就能夠了,即從UTF-8轉換到GB2312的函數u2g,連同反向轉換的函數g2u,源代碼在wxb_codeConv.c中:
/********************************************************************
created: 2007/11/15
created: 15:11:2007 10:30
filename: wxb_codeConv.c
author: Wang xuebin
depend: iconv.lib
build: 無(wú)需build,被包含到其他源代碼中
purpose: 提供從UTF-8到GB2312的內碼轉換,連同反向的轉換
*********************************************************************/
#include "iconv.h"
#include
//代碼轉換:從一種編碼轉為另一種編碼
int code_convert(char* from_charset, char* to_charset, char* inbuf,
int inlen, char* outbuf, int outlen)
{
iconv_t cd;
char** pin = &inbuf;
char** pout = &outbuf;
cd = iconv_open(to_charset,from_charset);
if(cd == 0)
return -1;
memset(outbuf,0,outlen);
if(iconv(cd,(const char**)pin,(unsigned int *)&inlen,pout,(unsigned int*)&outlen)
== -1)
return -1;
iconv_close(cd);
return 0;
}
//UNICODE碼轉為GB2312碼
//成功則返回一個(gè)動(dòng)態(tài)分配的char*變量,需要在使用完畢后手動(dòng)free,失敗返回NULL
char* u2g(char *inbuf)
{
int nOutLen = 2 * strlen(inbuf) - 1;
char* szOut = (char*)malloc(nOutLen);
if (-1 == code_convert("utf-8","gb2312",inbuf,strlen(inbuf),szOut,nOutLen))
{
free(szOut);
szOut = NULL;
}
return szOut;
}
//GB2312碼轉為UNICODE碼
//成功則返回一個(gè)動(dòng)態(tài)分配的char*變量,需要在使用完畢后手動(dòng)free,失敗返回NULL
char* g2u(char *inbuf)
{
int nOutLen = 2 * strlen(inbuf) - 1;
char* szOut = (char*)malloc(nOutLen);
if (-1 == code_convert("gb2312","utf-8",inbuf,strlen(inbuf),szOut,nOutLen))
{
free(szOut);
szOut = NULL;
}
return szOut;
}
使用的時(shí)候將這個(gè)c文檔include到其他源文檔中。include一個(gè)c文檔并不奇怪,在c語(yǔ)言的年代我們常常這么干,唯一的害處的編譯鏈接出來(lái)的可執行程式體積變大了。當然這時(shí)因為我們這段代碼很小的原因,再大一點(diǎn)我就要用dll了。
從UTF-8到GB2312的一個(gè)典型使用流程如下:
l 得到一個(gè)UTF-8的字符串szSrc;
l 定義一個(gè)char*的字符指針szDes,并無(wú)需給他動(dòng)態(tài)審判內存;
l szDes = u2g(szSrc),這樣就能夠得到轉換后的GB2312編碼的字符串;
l 使用完這個(gè)字符串后使用free(szDes)來(lái)釋放內存。
本文并不準備講述iconv中的函數細節,因為那幾個(gè)函數連同數據類(lèi)型都很簡(jiǎn)單,我們還是重點(diǎn)看一下如何在libxml2中使用編碼轉換來(lái)處理帶有中文的xml文檔。下面是使用以上方法來(lái)創(chuàng )建一個(gè)帶有中文的XML文檔的例子程式CreateXmlFile_cn.cpp,源代碼如下:
/********************************************************************
created: 2007/11/17
created: 9:11:2007 15:34
filename: CreateXmlFile.cpp
author: Wang xuebin
depend: libxml2.lib iconv.lib
build: nmake TARGET_NAME=CreateXmlFile_cn
purpose: 創(chuàng )建一個(gè)xml文檔,其中包含中文
*********************************************************************/
#include
#include
#include
#include
#include "wxb_codeConv.c" //自己寫(xiě)的編碼轉換函數
int main(int argc, char **argv)
{
//定義文檔和節點(diǎn)指針
xmlDocPtr doc = xmlNewDoc(BAD_CAST"1.0");
xmlNodePtr root_node = xmlNewNode(NULL,BAD_CAST"root");
//配置根節點(diǎn)
xmlDocSetRootElement(doc,root_node);
//一個(gè)中文字符串轉換為UTF-8字符串,然后寫(xiě)入
char* szOut = g2u("節點(diǎn)1的內容");
//在根節點(diǎn)中直接創(chuàng )建節點(diǎn)
xmlNewTextChild(root_node, NULL, BAD_CAST "newNode1", BAD_CAST "newNode1 content");
xmlNewTextChild(root_node, NULL, BAD_CAST "newNode2", BAD_CAST "newNode2 content");
xmlNewTextChild(root_node, NULL, BAD_CAST "newNode3", BAD_CAST "newNode3 content");
xmlNewChild(root_node, NULL, BAD_CAST "node1",BAD_CAST szOut);
free(szOut);
//創(chuàng )建一個(gè)節點(diǎn),配置其內容和屬性,然后加入根結點(diǎn)
xmlNodePtr node = xmlNewNode(NULL,BAD_CAST"node2");
xmlNodePtr content = xmlNewText(BAD_CAST"NODE CONTENT");
xmlAddChild(root_node,node);
xmlAddChild(node,content);
szOut = g2u("屬性值");
xmlNewProp(node,BAD_CAST"attribute",BAD_CAST szOut);
free(szOut);
//創(chuàng )建一個(gè)中文節點(diǎn)
szOut = g2u("中文節點(diǎn)");
xmlNewChild(root_node, NULL, BAD_CAST szOut,BAD_CAST "content of chinese node");
free(szOut);
//存儲xml文檔
int nRel = xmlSaveFormatFileEnc("CreatedXml_cn.xml",doc,"GB2312",1);
if (nRel != -1)
{
cout一個(gè)xml文檔被創(chuàng )建,寫(xiě)入"個(gè)字節" }
xmlFreeDoc(doc);
return 1;
}
編譯鏈接命令如下:
nmake TARGET_NAME=CreateXmlFile_cn
完成后執行CreateXmlFile_cn.exe能夠生成一個(gè)xml文檔CreatedXml_cn.xml,其內容如下:
root>
newNode1>newNode1 contentnewNode1>
newNode2>newNode2 contentnewNode2>
newNode3>newNode3 contentnewNode3>
node1>節點(diǎn)1的內容node1>
node2 attribute="屬性值">NODE CONTENTnode2>
中文節點(diǎn)>content of chinese node中文節點(diǎn)>
root>
觀(guān)察可知,節點(diǎn)的名稱(chēng)、內容、屬性都能夠使用中文了。在解析、修改和查找XML文檔時(shí)都能夠使用上面的方法,只要記住,進(jìn)入xml文檔之前將中文編碼轉換為UTF-8編碼;從XML中取出數據時(shí),不管三七二十一都能夠轉換為GB2312再用,否則您很有可能見(jiàn)到傳說(shuō)中的亂碼!
5. 用XML來(lái)做點(diǎn)什么
有了以上的基礎,相信已能夠順利的在c/c++程式中使用XML文檔了。那么,我們到底要用XML來(lái)做什么呢?我隨便說(shuō)一說(shuō)自己的想法:
第一,能夠用來(lái)作為配置文檔。例如很多組件就是用XML來(lái)做配置文檔;當然,我們知道用INI做配置文檔更簡(jiǎn)單,只要熟悉兩個(gè)函數就能夠了;但是,復雜一點(diǎn)的配置文檔我還是建議采用XML;
第二,能夠用來(lái)作為在程式之間傳送數據的格式,這樣的話(huà)最好給您的xml先定義一個(gè)XML Schema,這樣的數據首先能夠做一個(gè)良構校驗,還能夠來(lái)一個(gè)Schema校驗,如此的話(huà)出錯率會(huì )比沒(méi)有格式的數據小得多?,F在XML已廣泛作為網(wǎng)絡(luò )之間的數據格式了;
第三,能夠用來(lái)作為您自定義的數據存儲格式,例如對象持久化之類(lèi)的功能;
最后,能夠用來(lái)顯示您的技術(shù)很高深,本來(lái)您要存儲一個(gè)1,結果您這樣存儲了:
root>
My_Program_Code content="1">My_Program_Code>
root>
本文來(lái)自ChinaUnix博客,假如查看原文請點(diǎn):http://blog.chinaunix.net/u1/56834/showart_441723.html
以上內容由
華夏名網(wǎng) 收集整理,如轉載請注明原文出處,并保留這一部分內容。
“華夏名網(wǎng)” http://www.sudu.cn 和 http://www.bigwww.com 是成都飛數科技有限公司的網(wǎng)絡(luò )服務(wù)品牌,專(zhuān)業(yè)經(jīng)營(yíng)虛擬主機,域名注冊,VPS,服務(wù)器租賃業(yè)務(wù)。公司創(chuàng )建于2002年,經(jīng)過(guò)近5年的高速發(fā)展,“華夏名網(wǎng)”已經(jīng)成為我國一家知名的互聯(lián)網(wǎng)服務(wù)提供商,被國外權威機構webhosting.info評價(jià)為25大IDC服務(wù)商之一。