天天看點

C語言libxml用法,c語言libxml2庫的安裝和使用.....

寫這篇文章的原因有如下幾點:

1)C++标準庫中沒有操作XML的方法,用C++操作XML檔案必須熟悉一種函數庫,LIBXML2是其中一種很優秀的XML庫,而且它同時支援多種程式設計語言;

2)LIBXML2庫的Tutorial寫得不太好,尤其是編碼轉換的部分,不适用于中文編碼的轉換;3)網上的大多數關于Libxml2的介紹僅僅是翻譯了自帶的資料,沒有詳細介紹如何在windows平台下進行程式設計,更很少提到如何解決中文問題。

基于以上幾點原因,決定寫一個在Windows平台下,使用C/C++語言,應用LibXml2庫來進行xml文檔操作,同時使用ICONV庫進行中文編碼轉換的文檔。其中還涉及了Makefile、XPATH等相關内容。本文中所有的源代碼在http://www.blogjava.net/Files/wxb_nudt/xml_src.rar。

1.下載下傳與安裝LIBXML2和ICONV

Libxml2是一個C語言的XML程式庫,可以簡單友善的提供對XML文檔的各種操作,并且支援XPATH查詢,以及部分的支援XSLT轉換等功能。Libxml2的下載下傳位址是,完全版的庫是開源的,并且帶有例子程式和說明文檔。最好将這個庫先下載下傳下來,因為這樣可以檢視其中的文檔和例子。

windows版本的的下載下傳位址是;這個版本隻提供了頭檔案、庫檔案和dll,不包含源代碼、例子程式和文檔。在文本中,隻需要下載下傳libxml2庫、iconv庫和zlib庫就行了(注意,libxml2庫依賴iconv和zlib庫,本文中重點關注libxml2和iconv,zlib不介紹),我使用的版本是libxml2-2.6.30.win32.zip、zlib-1.2.3.win32.zip和iconv-1.9.2.win32.zip。

在程式設計的時候,我們使用windows版本的libxml2、zlib和iconv,将其解壓縮到指定檔案夾,例如D:"libxml2-2.6.30.win32,D:"zlib-1.2.3.win32以及D:"iconv-1.9.2.win32。事實上,我們知道在windows下面使用頭檔案、庫檔案和dll是不需要安裝的,它又沒有使用任何需要注冊的元件或者資料庫,隻需要告訴編譯器和連結器這些資源的位置就可以了。

注意:要在path變量中加上D:"iconv-1.9.2.win32"bin;D:"zlib-1.2.3.win32"bin;D:"libxml2-2.6.30.win32"bin這三個位址,否則在執行的時候就找不到。或者使用更簡單的方法,把其中的三個dll到拷貝到system32目錄中。

有兩種方法來編譯連結基于libxml2的程式,第一種是在VC環境中設定lib和include路徑,并在link設定中添加libxml2.lib和iconv.lib;第二種是用編譯器選項告訴編譯器cl.exe頭檔案的位置,并用連結器選項告訴連結器link.exe庫檔案的位置,同時在windows環境變量path中添加libxml2中bin檔案夾的位置,以便于程式運作時可以找到dll(也可以将dll拷貝到system32目錄下)。顯然我選擇了第二種,那麼編譯連結一個名為CreateXmlFile.cpp源檔案的指令如下:

cl /c /I D:"iconv-1.9.2.win32"include /I D:"libxml2-2.6.30.win32"include CreateXmlFile.cpp

link /libpath:D:"iconv-1.9.2.win32"lib /libpath:D:"libxml2-2.6.30.win32"lib CreateXmlFile.obj iconv.lib libxml2.lib

顯然這樣很費時,那麼再不用makefile就顯得矯情了,于是,一個典型的使用nmake.exe(VC自帶的makefile工具)的檔案如下:MAKEFILE

#

# 本目錄下所有源代碼的makefile,使用方法是nmake TARGET_NAME=源代碼檔案名字(不加字尾)

# 例如 nmake TARGET_NAME=CreateXmlFile

# Author: Wang Xuebin

#

# Flags - 編譯debug版本

#

#指定要使用的庫的路徑,需要使用者修改的變量一般放在makefile檔案的最上面

LIBXML2_HOME = D:"libxml2-2.6.30.win32

ICONV_HOME = D:"iconv-1.9.2.win32

#指定編譯器選項,/c表明cl指令隻編譯不連結;/MTd表明使用多線程debug庫;/Zi表明産生完整的調試資訊;

#/Od表明關閉編譯優化;/D _DEBUG表明定義一個名為_DEBUG的宏

CPP_FLAGS=/c /MTd /Zi /Od /D _DEBUG

#連結選項,/DEBUG表明建立Debug資訊

EXE_LINK_FLAGS=/DEBUG

#指定連結的庫

LIBS=iconv.lib libxml2.lib

#指定編譯路徑選項,連結路徑選項

INCLUDE_FLAGS= /I $(LIBXML2_HOME)"include /I $(ICONV_HOME)"include

LIB_PATH_FLAGS = /libpath:$(ICONV_HOME)"lib /libpath:$(LIBXML2_HOME)"lib

#################################################

#

# Targets 目标

#

$(TARGET_NAME) : $(TARGET_NAME).exe

clean : $(TARGET_NAME).exe

$(TARGET_NAME).obj : $(TARGET_NAME).cpp

cl $(CPP_FLAGS) $(INCLUDE_FLAGS) $(TARGET_NAME).cpp

$(TARGET_NAME).exe : $(TARGET_NAME).obj

link $(EXE_LINK_FLAGS) $(LIB_PATH_FLAGS) $(TARGET_NAME).obj $(LIBS)

clean : $(TARGET_NAME).exe

del $(TARGET_NAME).exe

del $(TARGET_NAME).obj

del $(TARGET_NAME).ilk

del $(TARGET_NAME).pdb

本文不準備介紹makefile的寫法,但後續例子程式的編譯連結依葫蘆畫瓢都沒有問題,執行編譯連結的指令如下:

nmake TARGET_NAME=CreateXmlFile

執行清理的指令如下:

nmake TARGET_NAME=CreateXmlFile clean

2.Libxml2中的資料類型和函數

一個函數庫中可能有幾百種資料類型以及幾千個函數,但是記住大師的話,90%的功能都是由30%的内容提供的。對于libxml2,我認為搞懂以下的資料類型和函數就足夠了。

2.1内部字元類型xmlChar

xmlChar是Libxml2中的字元類型,庫中所有字元、字元串都是基于這個資料類型。事實上它的定義是:xmlstring.h

typedef unsigned char xmlChar;

使用unsigned char作為内部字元格式是考慮到它能很好适應UTF-8編碼,而UTF-8編碼正是libxml2的内部編碼,其它格式的編碼要轉換為這個編碼才能在libxml2中使用。

還經常可以看到使用xmlChar*作為字元串類型,很多函數會傳回一個動态配置設定記憶體的xmlChar*變量,使用這樣的函數時記得要手動删除記憶體。

2.2xmlChar相關函數

如同标準c中的char類型一樣,xmlChar也有動态記憶體配置設定、字元串操作等相關函數。例如xmlMalloc是動态配置設定記憶體的函數;xmlFree是配套的釋放記憶體函數;xmlStrcmp是字元串比較函數等等。

基本上xmlChar字元串相關函數都在xmlstring.h中定義;而動态記憶體配置設定函數在xmlmemory.h中定義。

2.3xmlChar*與其它類型之間的轉換

另外要注意,因為總是要在xmlChar*和char*之間進行類型轉換,是以定義了一個宏BAD_CAST,其定義如下:xmlstring.h

#define BAD_CAST (xmlChar *)

原則上來說,unsigned char和char之間進行強制類型轉換是沒有問題的。

2.4文檔類型xmlDoc、指針xmlDocPtr

xmlDoc是一個struct,儲存了一個xml的相關資訊,例如檔案名、文檔類型、子節點等等;xmlDocPtr等于xmlDoc*,它搞成這個樣子總讓人以為是智能指針,其實不是,要手動删除的。

xmlNewDoc函數建立一個新的文檔指針。

xmlParseFile函數以預設方式讀入一個UTF-8格式的文檔,并傳回文檔指針。

xmlReadFile函數讀入一個帶有某種編碼的xml文檔,并傳回文檔指針;細節見libxml2參考手冊。

xmlFreeDoc釋放文檔指針。特别注意,當你調用xmlFreeDoc時,該文檔所有包含的節點記憶體都被釋放,是以一般來說不需要手動調用xmlFreeNode或者xmlFreeNodeList來釋放動态配置設定的節點記憶體,除非你把該節點從文檔中移除了。一般來說,一個文檔中所有節點都應該動态配置設定,然後加入文檔,最後調用xmlFreeDoc一次釋放所有節點申請的動态記憶體,這也是為什麼我們很少看見xmlNodeFree的原因。

xmlSaveFile将文檔以預設方式存入一個檔案。

xmlSaveFormatFileEnc可将文檔以某種編碼/格式存入一個檔案中。

2.5節點類型xmlNode、指針xmlNodePtr

節點應該是xml中最重要的元素了,xmlNode代表了xml文檔中的一個節點,實作為一個struct,内容很豐富:tree.h

typedef struct _xmlNode xmlNode;

typedef xmlNode *xmlNodePtr;

struct _xmlNode {

void           *_private;

xmlElementType   type;  

const xmlChar   *name;     

struct _xmlNode *children; 

struct _xmlNode *last;  

struct _xmlNode *parent;

struct _xmlNode *next;  

struct _xmlNode *prev;  

struct _xmlDoc *doc;

xmlNs           *ns;       

xmlChar         *content;  

struct _xmlAttr *properties;

xmlNs           *nsDef;    

void            *psvi;

unsigned short   line;  

unsigned short   extra; 

};

可以看到,節點之間是以連結清單和樹兩種方式同時組織起來的,next和prev指針可以組成連結清單,而parent和children可以組織為樹。同時還有以下重要元素:

l節點中的文字内容:content;

l節點所屬文檔:doc;

l節點名字:name;

l節點的namespace:ns;

l節點屬性清單:properties;

Xml文檔的操作其根本原理就是在節點之間移動、查詢節點的各項資訊,并進行增加、删除、修改的操作。

xmlDocSetRootElement函數可以将一個節點設定為某個文檔的根節點,這是将文檔與節點連接配接起來的重要手段,當有了根結點以後,所有子節點就可以依次連接配接上根節點,進而組織成為一個xml樹。

2.6節點集合類型xmlNodeSet、指針xmlNodeSetPtr

節點集合代表一個由節點組成的變量,節點集合隻作為Xpath的查詢結果而出現(XPATH的介紹見後面),是以被定義在xpath.h中,其定義如下:

typedef struct _xmlNodeSet xmlNodeSet;

typedef xmlNodeSet *xmlNodeSetPtr;

struct _xmlNodeSet {

int nodeNr;         

int nodeMax;     

xmlNodePtr *nodeTab;

};

可以看出,節點集合有三個成員,分别是節點集合的節點數、最大可容納的節點數,以及節點數組頭指針。對節點集合中各個節點的通路方式很簡單,如下:

xmlNodeSetPtr nodeset = XPATH查詢結果;

for (int i = 0; i < nodeset->nodeNr; i++)

{

nodeset->nodeTab[i];

}

注意,libxml2是一個c函數庫,是以其函數和資料類型都使用c語言的方式來處理。如果是c++,我想我甯願用STL中的vector來表示一個節點集合更好,而且沒有記憶體洩漏或者溢出的擔憂。

3.簡單xml操作例子

了解以上基本知識之後,就可以進行一些簡單的xml操作了。當然,還沒有涉及到内碼轉換(使得xml中可以進行中文)、xpath等較複雜的操作。

3.1建立xml文檔

有了上面的基礎,建立一個xml文檔顯得非常簡單,其流程如下:

l用xmlNewDoc函數建立一個文檔指針doc;

l用xmlNewNode函數建立一個節點指針root_node;

l用xmlDocSetRootElement将root_node設定為doc的根結點;

l給root_node添加一系列的子節點,并設定子節點的内容和屬性;

l用xmlSaveFile将xml文檔存入檔案;

l用xmlFreeDoc函數關閉文檔指針,并清除本文檔中所有節點動态申請的記憶體。

注意,有多種方式可以添加子節點:第一是用xmlNewTextChild直接添加一個文本子節點;第二是先建立新節點,然後用xmlAddChild将新節點加入上層節點。

源代碼檔案是CreateXmlFile.cpp,如下:

#include

#include

#include

#include

int main()

{

//定義文檔和節點指針

xmlDocPtr doc = xmlNewDoc(BAD_CAST"1.0");

xmlNodePtr root_node = xmlNewNode(NULL,BAD_CAST"root");

//設定根節點

xmlDocSetRootElement(doc,root_node);

//在根節點中直接建立節點

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");

//建立一個節點,設定其内容和屬性,然後加入根結點

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");

//建立一個兒子和孫子節點

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<

}

//釋放文檔内節點動态申請的記憶體

xmlFreeDoc(doc);

return 1;

}

編譯連結指令如下:

nmake TARGET_NAME=CreateXmlFile

然後執行可執行檔案CreateXmlFile.exe,會生成一個xml檔案CreatedXml.xml,打開後如下所示:

newNode1 content

newNode2 content

newNode3 content

NODE CONTENT

This is a grandson node

最好使用類似XMLSPY這樣的工具打開,因為這些工具可以自動整理xml檔案的栅格,否則很有可能是沒有任何換行的一個xml檔案,可讀性較差。

3.2解析xml文檔

解析一個xml文檔,從中取出想要的資訊,例如節點中包含的文字,或者某個節點的屬性,其流程如下:

l用xmlReadFile函數讀出一個文檔指針doc;

l用xmlDocGetRootElement函數得到根節點curNode;

lcurNode->xmlChildrenNode就是根節點的子節點集合;

l輪詢子節點集合,找到所需的節點,用xmlNodeGetContent取出其内容;

l用xmlHasProp查找含有某個屬性的節點;

l取出該節點的屬性集合,用xmlGetProp取出其屬性值;

l用xmlFreeDoc函數關閉文檔指針,并清除本文檔中所有節點動态申請的記憶體。

注意:節點清單的指針依然是xmlNodePtr,屬性清單的指針也是xmlAttrPtr,并沒有xmlNodeList或者xmlAttrList這樣的類型。看作清單的時候使用它們的next和prev連結清單指針來進行輪詢。隻有在Xpath中有xmlNodeSet這種類型,其使用方法前面已經介紹了。

源代碼如下:ParseXmlFile.cpp

#include

#include

int main(int argc, char* argv[])

{

xmlDocPtr doc;           //定義解析文檔指針

xmlNodePtr curNode;      //定義結點指針(你需要它為了在各個結點間移動)

xmlChar *szKey;          //臨時字元串變量

char *szDocName;

if (argc <= 1)

{

printf("Usage: %s docname"n", argv[0]);

return(0);

}

szDocName = argv[1];

doc = xmlReadFile(szDocName,"GB2312",XML_PARSE_RECOVER); //解析檔案

//檢查解析文檔是否成功,如果不成功,libxml将指一個注冊的錯誤并停止。

//一個常見錯誤是不适當的編碼。XML标準文檔除了用UTF-8或UTF-16外還可用其它編碼儲存。

//如果文檔是這樣,libxml将自動地為你轉換到UTF-8。更多關于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;

}

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)

{

//取出節點中的内容

if ((!xmlStrcmp(curNode->name, (const xmlChar *)"newNode1")))

{

szKey = xmlNodeGetContent(curNode);

printf("newNode1: %s"n", szKey);

xmlFree(szKey);

}

//查找帶有屬性attribute的節點

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

執行指令如下,使用第一次建立的xml檔案作為輸入:

ParseXmlFile.exe CreatedXml.xml

觀察源代碼可發現,所有以查詢方式得到的xmlChar*字元串都必須使用xmlFree函數手動釋放。否則會造成記憶體洩漏。

3.3修改xml文檔

有了上面的基礎,修改xml文檔的内容就很簡單了。首先打開一個已經存在的xml文檔,順着根結點找到需要添加、删除、修改的地方,調用相應的xml函數對節點進行增、删、改操作。源代碼見ChangeXmlFile,編譯連結方法如上。執行下面的指令:

ChangeXmlFile.exe CreatedXml.xml

可以得到一個修改後的xml文檔ChangedXml.xml,如下:

content changed

newNode3 content

NODE CONTENT

This is a grandson node

new content

需要注意的是,并沒有xmlDelNode或者xmlRemoveNode函數,我們删除節點使用的是以下一段代碼:

if (!xmlStrcmp(curNode->name, BAD_CAST "newNode1"))

{

xmlNodePtr tempNode;

tempNode = curNode->next;

xmlUnlinkNode(curNode);

xmlFreeNode(curNode);

curNode = tempNode;

continue;

}

即将目前節點從文檔中斷鍊(unlink),這樣本文檔就不會再包含這個子節點。這樣做需要使用一個臨時變量來存儲斷鍊節點的後續節點,并記得要手動删除斷鍊節點的記憶體。

3.4使用XPATH查找xml文檔

簡而言之,XPATH之于xml,好比SQL之于關系資料庫。要在一個複雜的xml文檔中查找所需的資訊,XPATH簡直是必不可少的工具。XPATH

使用XPATH之前,必須首先熟悉幾個資料類型和函數,它們是使用XPATH的前提。在libxml2中使用Xpath是非常簡單的,其流程如下:

l定義一個XPATH上下文指針xmlXPathContextPtr context,并且使用xmlXPathNewContext函數來初始化這個指針;

l定義一個XPATH對象指針xmlXPathObjectPtr result,并且使用xmlXPathEvalExpression函數來計算Xpath表達式,得到查詢結果,将結果存入對象指針中;

l使用result->nodesetval得到節點集合指針,其中包含了所有符合Xpath查詢結果的節點;

l使用xmlXPathFreeContext釋放上下文指針;

l使用xmlXPathFreeObject釋放Xpath對象指針;

具體的使用方法可以看XpathForXmlFile.cpp的這一段代碼,其功能是查找符合某個Xpath語句的對象指針:

xmlXPathObjectPtr getNodeSet(xmlDocPtr doc, const xmlChar *szXpath)

{

xmlXPathContextPtr context;    //XPATH上下文指針

xmlXPathObjectPtr result;       //XPATH對象指針,用來存儲查詢結果

context = xmlXPathNewContext(doc);     //建立一個XPath上下文指針

if (context == NULL)

{

printf("context is NULL"n");

return NULL;

}

result = xmlXPathEvalExpression(szXpath, context); //查詢XPath表達式,得到一個查詢結果

xmlXPathFreeContext(context);             //釋放上下文指針

if (result == NULL)

{

printf("xmlXPathEvalExpression return NULL"n");

return NULL;

}

if (xmlXPathNodeSetIsEmpty(result->nodesetval))   //檢查查詢結果是否為空

{

xmlXPathFreeObject(result);

printf("nodeset is empty"n");

return NULL;

}

return result;

}

一個完整的使用Xpath的例子在代碼XpathForXmlFile.cpp中,它查找一個xml檔案中符合"/root/node2[@attribute='yes']"語句的結果,并且将找到的節點的屬性和内容列印出來。編譯連結指令如下:

nmake TARGET_NAME=XpathForXmlFile

執行方式如下:

XpathForXmlFile.exe CreatedXml.xml

觀察結果可以看出找到了一個節點,即root下面node2節點,它的attribute屬性值正好等于yes。更多關于Xpath的内容可以參考XPATH官方手冊。隻有掌握了XPATH,才掌握了使用大型XML檔案的方法,否則每尋找一個節點都要從根節點找起,會把人累死。

4.用ICONV解決XML中的中文問題

Libxml2中預設的内碼是UTF-8,所有使用libxml2進行處理的xml檔案,必須首先顯式或者預設的轉換為UTF-8編碼才能被處理。

要在xml中使用中文,就必須能夠在UTF-8和GB2312内碼(較常用的一種簡體中文編碼)之間進行轉換。Libxml2提供了預設的内碼轉換機制,并且在libxml2的Tutorial中有一個例子,事實證明這個例子并不适合用來轉換中文。

是以需要我們顯式的使用ICONV來進行内碼轉換,libxml2本身也是使用ICONV進行轉換的。ICONV是一個專門用來進行編碼轉換的庫,基本上支援目前所有常用的編碼。它是glibc庫的一個部分,常常被用于UNIX系統中。當然,在windows下面使用也沒有任何問題。前面已經提到了ICONV的安裝和使用方法,這裡主要講一下程式設計相關問題。

本節其實和xml以及libxml2沒有太大關系,你可以把它簡單看作是一個編碼轉換方面的專題。我們僅僅需要學會使用兩個函數就可以了,即從UTF-8轉換到GB2312的函數u2g,以及反向轉換的函數g2u,源代碼在wxb_codeConv.c中:

#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碼

//成功則傳回一個動态配置設定的char*變量,需要在使用完畢後手動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碼

//成功則傳回一個動态配置設定的char*變量,需要在使用完畢後手動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;

}

使用的時候将這個c檔案include到其它源檔案中。include一個c檔案并不奇怪,在c語言的年代我們常常這麼幹,唯一的害處的編譯連結出來的可執行程式體積變大了。當然這時因為我們這段代碼很小的原因,再大一點我就要用dll了。

從UTF-8到GB2312的一個典型使用流程如下:

l得到一個UTF-8的字元串szSrc;

l定義一個char*的字元指針szDes,并不需要給他動态審判記憶體;

lszDes = u2g(szSrc),這樣就可以得到轉換後的GB2312編碼的字元串;

l使用完這個字元串後使用free(szDes)來釋放記憶體。

本文并不準備講述iconv中的函數細節,因為那幾個函數以及資料類型都非常簡單,我們還是重點看一下如何在libxml2中使用編碼轉換來處理帶有中文的xml檔案。下面是使用以上方法來建立一個帶有中文的XML檔案的例子程式CreateXmlFile_cn.cpp,源代碼如下:

#include

#include

#include

#include

#include "wxb_codeConv.c" //自己寫的編碼轉換函數

int main(int argc, char **argv)

{

//定義文檔和節點指針

xmlDocPtr doc = xmlNewDoc(BAD_CAST"1.0");

xmlNodePtr root_node = xmlNewNode(NULL,BAD_CAST"root");

//設定根節點

xmlDocSetRootElement(doc,root_node);

//一個中文字元串轉換為UTF-8字元串,然後寫入

char* szOut = g2u("節點1的内容");

//在根節點中直接建立節點

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);

//建立一個節點,設定其内容和屬性,然後加入根結點

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);

//建立一個中文節點

szOut = g2u("中文節點");

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<

}

xmlFreeDoc(doc);

return 1;

}

編譯連結指令如下:

nmake TARGET_NAME=CreateXmlFile_cn

完成後執行CreateXmlFile_cn.exe可以生成一個xml檔案CreatedXml_cn.xml,其内容如下:

newNode1 content

newNode2 content

newNode3 content

節點1的内容

NODE CONTENT

content of chinese node中文節點>

觀察可知,節點的名稱、内容、屬性都可以使用中文了。在解析、修改和查找XML文檔時都可以使用上面的方法,隻要記住,進入xml文檔之前将中文編碼轉換為UTF-8編碼;從XML中取出資料時,不管三七二十一都可以轉換為GB2312再用,否則你很有可能見到傳說中的亂碼!

5.用XML來做點什麼

有了以上的基礎,相信已經可以順利的在c/c++程式中使用XML文檔了。那麼,我們到底要用XML來做什麼呢?我随便說一說自己的想法:

第一,可以用來作為配置檔案。例如很多元件就是用XML來做配置檔案;當然,我們知道用INI做配置檔案更簡單,隻要熟悉兩個函數就可以了;不過,複雜一點的配置檔案我還是建議采用XML;

第二,可以用來作為在程式之間傳送資料的格式,這樣的話最好給你的xml先定義一個XML Schema,這樣的資料首先可以做一個良構校驗,還可以來一個Schema校驗,如此的話出錯率會比沒有格式的資料小得多。目前XML已經廣泛作為網絡之間的資料格式了;

第三,可以用來作為你自定義的資料存儲格式,例如對象持久化之類的功能;

最後,可以用來顯示你的技術很高深,本來你要存儲一個1,結果你這樣存儲了:

然後再用libxml2取出來,最好還來幾次編碼轉換,是不是讓人覺得你很牛呢,哈哈!說笑了,千萬不要這麼做。

6.小結

本文是實用程式設計技術的第四篇,有興趣的可以看看我的前三篇:

另外,關于XML也可以看看我寫的這幾篇部落格:

原文連結:http://blog.csdn.net/hanchaoman/article/details/6782034