文章來自:http://blog.csdn.net/shanzhizi/article/details/7726679
一、下載和安裝LIBXML2
【方法一】
Libxml2是個C語言的XML程式庫,能簡單方便的提供對XML文件的各種操作,並且支持XPATH查詢,及部分的支持XSLT轉換等功能。Libxml2的下載地址是
http://xmlsoft.org/
完全版的庫是開源的,並且帶有例子程式和說明文件。由於我是在linux下用C語言進行研發的,所以我下載的是libxml2-2.6.20.tar.gz版本的源碼包。
具體安裝步驟:
1、解壓:$tar zxvf libxml2-2.6.20.tar.gz
2、進入解壓后的安裝目錄:$cd libxml2-2.6.20
3、安裝三部曲:1)$./configure
2)$make
3)$make install
安裝完畢。
libxml2安裝中出現的錯誤:
cannot remove `libtoolT': No such file or directory
解決方法:
修改configure文件
$ vim configure
刪除這一行: $RM "$cfgfile"
重新再運行 $ ./configure
你在安裝libxml2的時候,./configure --prefix=/usr/local
你在安裝好后,需要重新定義:
export LD_LIBRARY_PATH=/usr/local/lib
export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
具體的位置根據prefix指定的路徑定。
【方法二】
#sudo apt-get install libxml2
#sudo apt-get install libxml2-dev
【方法三】
libxml2 安裝使用 收藏
在項目中需要對數據的保存與交換,為了方便期間使用普通文本保存。但對文本解析時,考慮到程序的健壯性就需要處理多種意外情況,比較麻煩,因此對xml產生了點興趣。xml本身網站上有大把的資料,可以隨時參考。
Gnome項目提供了一個xml的C解析器:Libxml2。libxml2最新提供了xmlreader接口,使用很方便,不過版本在2.6以上才支持。今天倒騰了一了下,做了下簡單測試,在編譯自己的小程序上被拌了一下,寫文紀念。
現在的最新版本是2.6.30,可以到http://ftp.gnome.org/pub/GNOME/sources/libxml2/2.6/ 下載。
安裝很簡單,三部走。
view plaincopy to clipboardprint?
01.// install
02.#./configure
03.#make
04.#make install
05.//test install
06.#make tests
07.//uninstall
08.#make uninstall
// install
#./configure
#make
#make install
//test install
#make tests
//uninstall
#make uninstall
如果不需要特別的定制,在configure階段可以直接默認。 libxml2將默認把頭文件與庫安裝在/usr/local/include/libxml2/libxml目錄下。因此可能會讓你在第一次編譯自己的程序時遇到頭文件“no such file”的錯誤(比如我自己就這么笨╮(╯▽╰)╭)。
libxml2提供了解決方法,它很體貼地在/usr/local/bin目錄下為您提供了xml2-config、xmlcatalog、xmllint三個便利的工具(嘀咕下:其實一點都不便利,提前給人說下多好啊,還得害人去查資料)。其中xml2-config在編譯時用得到。
xml2-config可以。。。自己看下面吧
view plaincopy to clipboardprint?
01.[root@Amanda ~]# xml2-config
02.Usage: xml2-config [OPTION]
03.Known values for OPTION are:
04. --prefix=DIR change libxml prefix [default /usr/local]
05. --exec-prefix=DIR change libxml exec prefix [default /usr/local]
06. --libs print library linking information
07. --cflags print pre-processor and compiler flags
08. --modules module support enabled
09. --help display this help and exit
10. --version output version information
[root@Amanda ~]# xml2-config
Usage: xml2-config [OPTION]
Known values for OPTION are:
--prefix=DIR change libxml prefix [default /usr/local]
--exec-prefix=DIR change libxml exec prefix [default /usr/local]
--libs print library linking information
--cflags print pre-processor and compiler flags
--modules module support enabled
--help display this help and exit
--version output version information
這里說到編譯時用到的參數: --cflags和--libs,幫助上說明這個為工程編譯時提供輔助。它們提供的信息如下:
view plaincopy to clipboardprint?
01.[root@Amanda ~]# xml2-config --cflags
02.-I/usr/local/include/libxml2
03.[root@Amanda ~]# xml2-config --libs
04.-L/usr/local/lib -lxml2 -lz -lm
[root@Amanda ~]# xml2-config --cflags
-I/usr/local/include/libxml2
[root@Amanda ~]# xml2-config --libs
-L/usr/local/lib -lxml2 -lz -lm
所以在編譯自己的測試程序時,可以直接使用 #gcc -I /usr/local/include/libxml2 -L/usr/local/lib -lxml2 -lz -lm -o test test.c 。
或者加入到makefile中,例如:
CFLAGS=`xml2-config --cflags`
LIBS=`xml2-config --libs`
測試程序:
#include <libxml/parser.h>
#include <libxml/tree.h>
int main(int argc, char **argv)
{
xmlDocPtr doc = NULL;
xmlNodePtr root_node = NULL, node = NULL, node1 = NULL;
doc = xmlNewDoc(BAD_CAST "1.0");
root_node = xmlNewNode(NULL, BAD_CAST "root");
xmlDocSetRootElement(doc, root_node);
xmlNewChild(root_node, NULL, BAD_CAST "node1",BAD_CAST "content of node1");
node=xmlNewChild(root_node, NULL, BAD_CAST "node3",BAD_CAST"node has attributes");
xmlNewProp(node, BAD_CAST "attribute", BAD_CAST "yes");
node = xmlNewNode(NULL, BAD_CAST "node4");
node1 = xmlNewText(BAD_CAST"other way to create content");
xmlAddChild(node, node1);
xmlAddChild(root_node, node);
xmlSaveFormatFileEnc(argc > 1 ? argv[1] : "-", doc, "UTF-8", 1);
xmlFreeDoc(doc);
xmlCleanupParser();
xmlMemoryDump();
return(0);
}
用下面的命令編譯通過。
gcc -I /usr/local/include/libxml2 -L /usr/local/lib -lxml2 test.c -o test
二、Libxml2中的數據類型和函數
一個函數庫中可能有幾百種數據類型及幾千個函數,不過記住大師的話,90%的功能都是由30%的內容提供的。對於libxml2(http://blog.csdn.net/shanzhizi),我認為搞懂以下的數據類型和函數就足夠了。
2.1 內部字符類型xmlChar
xmlChar是Libxml2中的字符類型,庫中所有字符、字符串都是基於這個數據類型。事實上他的定義是:xmlstring.h
typedef unsigned char xmlChar;
使用unsigned char作為內部字符格式是考慮到他能非常好適應UTF-8編碼,而UTF-8編碼正是libxml2的內部編碼,其他格式的編碼要轉換為這個編碼才能在libxml2中使用。
還經常能看到使用xmlChar*作為字符串類型,非常多函數會返回一個動態分配內存的xmlChar*變量,使用這樣的函數時記得要手動刪除內存。
2.2 xmlChar相關函數
如同標准c中的char類型相同,xmlChar也有動態內存分配、字符串操作等相關函數。例如xmlMalloc是動態分配內存的函數;xmlFree是配套的釋放內存函數;xmlStrcmp是字符串比較函數等等。
基本上xmlChar字符串相關函數都在xmlstring.h中定義;而動態內存分配函數在xmlmemory.h中定義。
2.3 xmlChar*和其他類型之間的轉換
另外要注意,因為總是要在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;/* 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 */
};
能看到,節點之間是以鏈表和樹兩種方式同時組織起來的,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中,其定義如下:
/*
* 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 @@ */
};
能看出,節點集合有三個成員,分別是節點集合的節點數、最大可容納的節點數,及節點數組頭指針。對節點集合中各個節點的訪問方式非常簡單,如下:
xmlNodeSetPtr nodeset = XPATH查詢結果;
for (int i = 0; i nodeNr; i++)
{
nodeset->nodeTab;
}
注意,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,如下:
/********************************************************************
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: 創建一個xml文件
*********************************************************************/
#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一個xml文件被創建,寫入"個字節" }
//釋放文件內節點動態申請的內存
xmlFreeDoc(doc);
return 1;
}
編譯鏈接命令如下:
nmake TARGET_NAME=CreateXmlFile
然后執行可執行文件CreateXmlFile.exe,會生成一個xml文件CreatedXml.xml,打開后如下所示:
root>
newNode1>newNode1 contentnewNode1>
newNode2>newNode2 contentnewNode2>
newNode3>newNode3 contentnewNode3>
node2 attribute="yes">NODE CONTENTnode2>
son>
grandson>This is a grandson nodegrandson>
son>
root>
最佳使用類似XMLSPY這樣的工具打開,因為這些工具能自動整理xml文件的柵格,否則非常有可能是沒有所有換行的一個xml文件,可讀性較差。
3.2 解析xml文件
解析一個xml文件,從中取出想要的信息,例如節點中包含的文字,或某個節點的屬性,其流程如下:
l 用xmlReadFile函數讀出一個文件指針doc;
l 用xmlDocGetRootElement函數得到根節點curNode;
l curNode->xmlChildrenNode就是根節點的子節點集合;
l 輪詢子節點集合,找到所需的節點,用xmlNodeGetContent取出其內容;
l 用xmlHasProp查找含有某個屬性的節點;
l 取出該節點的屬性集合,用xmlGetProp取出其屬性值;
l 用xmlFreeDoc函數關閉文件指針,並清除本文件中所有節點動態申請的內存。
注意:節點列表的指針依然是xmlNodePtr,屬性列表的指針也是xmlAttrPtr,並沒有xmlNodeList或xmlAttrList這樣的類型。看作列表的時候使用他們的next和prev鏈表指針來進行輪詢。只有在Xpath中有xmlNodeSet這種類型,其使用方法前面已介紹了。
原始碼如下: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; //定義結點指針(你需要他為了在各個結點間移動)
xmlChar *szKey; //臨時字符串變量
char *szDocName;
if (argc {
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;
}
/*在這個例子中,我們需要確認文件是正確的類型。“root”是在這個示例中使用文件的根類型。*/
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,如下:
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>
需要注意的是,並沒有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語法簡單易學,並且有一個非常好的官方教程,見
http://www.zvon.org/xxl/XPathTutorial/Output_chi/introduction.html
。這個站點的XML各種教程齊全,並且有包括中文在內的各國語言版本,真是讓我喜歡到非常!
使用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中:
/********************************************************************
created: 2007/11/15
created: 15:11:2007 10:30
filename: wxb_codeConv.c
author: Wang xuebin
depend: iconv.lib
build: 不必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碼
//成功則返回一個動態分配的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,並不必給他動態審判內存;
l szDes = u2g(szSrc),這樣就能得到轉換后的GB2312編碼的字符串;
l 使用完這個字符串后使用free(szDes)來釋放內存。
本文並不准備講述iconv中的函數細節,因為那幾個函數及數據類型都非常簡單,我們還是重點看一下怎么在libxml2中使用編碼轉換來處理帶有中文的xml文件。下面是使用以上方法來創建一個帶有中文的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: 創建一個xml文件,其中包含中文
*********************************************************************/
#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一個xml文件被創建,寫入"個字節" }
xmlFreeDoc(doc);
return 1;
}
編譯鏈接命令如下:
nmake TARGET_NAME=CreateXmlFile_cn
完成后執行CreateXmlFile_cn.exe能生成一個xml文件CreatedXml_cn.xml,其內容如下:
root>
newNode1>newNode1 contentnewNode1>
newNode2>newNode2 contentnewNode2>
newNode3>newNode3 contentnewNode3>
node1>節點1的內容node1>
node2 attribute="屬性值">NODE CONTENTnode2>
中文節點>content of chinese node中文節點>
root>
觀察可知,節點的名稱、內容、屬性都能使用中文了。在解析、修改和查找XML文件時都能使用上面的方法,只要記住,進入xml文件之前將中文編碼轉換為UTF-8編碼;從XML中取出數據時,不管三七二十一都能轉換為GB2312再用,否則你非常有可能見到傳說中的亂碼!
5. 用XML來做點什么
有了以上的基礎,相信已能順利的在c/c++程式中使用XML文件了。那么,我們到底要用XML來做什么呢?我隨便說一說自己的想法:
第一,能用來作為設置文件。例如非常多組件就是用XML來做設置文件;當然,我們知道用INI做設置文件更簡單,只要熟悉兩個函數就能了;不過,復雜一點的設置文件我還是建議采用XML;
第二,能用來作為在程式之間傳送數據的格式,這樣的話最佳給你的xml先定義一個XML Schema,這樣的數據首先能做一個良構校驗,還能來一個Schema校驗,如此的話出錯率會比沒有格式的數據小得多。目前XML已廣泛作為網絡之間的數據格式了;
第三,能用來作為你自定義的數據存儲格式,例如對象持久化之類的功能;
最后,能用來顯示你的技術非常高深,本來你要存儲一個1,結果你這樣存儲了:
root>
My_Program_Code content="1">My_Program_Code>
root>