前言:今天在作一個小項目時,客戶要求的xml,跟如今有系統要求的不同,因此要本身從新寫函數支持返回,進行簡單總結,但願對你們有所幫助。node
首先,使用xml函數須要鏈上動態庫libxml2,須要在電腦上安裝libxml的開發包,安裝方法以下:shell
(1)相關函數有許多,網上也有特別多的解釋,你們能夠百度一下,這裏只是簡單介紹一部分;建立一個XML文檔很是簡單,其流程以下:函數
① 用xmlNewDoc函數建立一個文檔指針doc。優化
② 用xmlNewNode函數建立一個節點指針root_node。編碼
③ 用xmlDocSetRootElement將root_node設置爲doc的根結點。spa
④ 給root_node添加一系列的子節點,並設置子節點的內容和屬性。命令行
⑤ 用xmlSaveFile將XML文檔存入文件(用xmlDocDumpFormatMemoryEnc將XML存入內存)。指針
⑥ 用xmlFreeDoc關閉文檔指針,並清除本文檔中全部節點動態申請的內存。code
有多種方式能夠添加子節點,如能夠用xmlNewTextChild直接添加一個文本子節點。也能夠先建立新節點,而後用xmlAddChild將新節點加入到上層節點中。orm
注:xmlSaveFile存入文件方便單獨執行程序查看結果,通常項目用用xmlDocDumpFormatMemoryEnc將XML存入內存!
(2)建立xml文件舉例
#include <stdio.h> #include <libxml/parser.h> #include <libxml/tree.h> int main() { xmlChar *result = NULL; int size = 0; 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")); xmlNodePtr congson = xmlNewNode(NULL, BAD_CAST "congson"); xmlAddChild(grandson,congson); //存儲xml文檔 //xmlKeepBlanksDefault(0); //xmlDocDumpFormatMemoryEnc(doc, &result, &size, "UTF-8", 1); int nRel = xmlSaveFile("CreateXml.xml",doc); if (nRel != -1) { printf("一個xml文檔被建立,寫入%d個字節\n", nRel); } //釋放文檔內節點動態申請的內存 xmlFreeDoc(doc); return 1; }
CentOS系統下面執行:gcc CreateXmlFile.c -o CreateXmlFile -I /usr/include/libxml2 -lxml2
執行./CreateXmlFile,會生成一個XML文件CreatedXml.xml。
(1)XML解析流程
解析一個XML文檔,從中取出想要的信息,例如節點中包含的文字,或者某個節點的屬性。其流程以下:
① 用xmlReadFile函數讀入一個文件,並返回一個文檔指針doc。
② 用xmlDocGetRootElement函數獲得根節點curNode。
③ 此時curNode->xmlChildrenNode就是根節點的首個兒子節點,該兒子節點的兄弟節點可用next指針進行輪詢。
④ 輪詢全部子節點,找到所需的節點,用xmlNodeGetContent取出其內容。
⑤ 用xmlHasProp查找含有某個屬性的節點,屬性列表指針xmlAttrPtr將指向該節點的屬性列表。
⑥ 取出該節點的屬性,用xmlGetProp取出其屬性值。
⑦ xmlFreeDoc函數關閉文檔指針,並清除本文檔中全部節點動態申請的內存。
(2). 解析XML文件並獲取屬性示例
#include <stdio.h> #include <libxml/parser.h> #include <libxml/tree.h> int main(int argc, char* argv[]) { xmlDocPtr doc; //定義解析文件指針 xmlNodePtr curNode; //定義結點指針 xmlChar *szKey; //臨時字符串變量 char *szDocName; if (argc <= 1) { printf("Usage: %s docname", argv[0]); return(0); } szDocName = argv[1]; doc = xmlReadFile(szDocName,"GB2312",XML_PARSE_RECOVER); //解析文件 //檢查解析文檔是否成功,若是不成功,libxml將報錯並中止解析。 //一個常見錯誤是不適當的編碼,XML標準文檔除了用UTF-8或UTF-16外還可用其它編碼保存 if (NULL == doc) { fprintf(stderr,"Document not parsed successfully."); return -1; } //獲取根節點 curNode = xmlDocGetRootElement(doc); if (NULL == curNode) { fprintf(stderr,"empty document"); 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"); printf("get attribute=%s\n", szAttr) ; xmlFree(szAttr); } attrPtr = attrPtr->next; } xmlFreeDoc(doc); return 0; }
編譯:gcc ParseXmlFile.c -o ParseXmlFile -I /usr/include/libxml2 -lxml2
執行:./ParseXmlFile xxx.xml
三、用iconv解決XML中字符集問題
libxml2中默認的內碼是UTF-8,全部使用libxml2進行處理的xml文件,必須首先顯式或者默認轉換爲UTF-8編碼才能被處理。
要在XML中使用中文,就必須可以在UTF-8和GB2312之間進行轉換。libxml2提供了默認的內碼轉換機制,而且在libxml2的Tutorial中有一個例子,事實證實這個例子並不很適合用來轉換中文。
有些場合須要使用iconv來進行編碼轉換,libxml2自己也是使用iconv進行編碼轉換的。iconv是一個專門用來進行編碼轉換的庫,基本上支持目前全部經常使用的編碼,它是glibc庫的一個部分。
本節其實和libxml沒有太大關係,能夠把它簡單看做是一個編碼轉換方面的專題。下文提供了一個通用轉碼函數,並在此基礎上實現了兩個轉碼封裝函數,即從UTF-8轉換到GB2312的函數u2g,以及反向轉換的函數g2u。其代碼以下:
#include <iconv.h> #include <string.h> //代碼轉換,從一種編碼轉爲另外一種編碼 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; }
下面以UTF-8到GB2312轉碼流程說明上文中轉碼函數的使用,使用流程以下:
① 獲得一個UTF-8的字符串szSrc。
② 定義一個char *的字符指針szDes,並不須要給它動態申請內存。
③ 調用szDes = u2g(szSrc),這樣szDes就指向轉換後GB2312編碼的字符串。
④ 使用完這個字符串後使用free(szDes)來釋放內存。
Linux系統下有個iconv命令,能夠在shell命令行輸入iconv --help 來查看用法,在程序中能夠採用system系統調用來進行文件轉碼。下文中f表示from,t表示to,其轉碼方法以下:
system("iconv –f 源格式 –t 目標格式 源文件 >目標文件")
system("iconv –f GB18030 –t UTF-8 test_gb.txt > test_utf.txt")
shell命令行使用方法: iconv -f gb2312 -t utf-8 readme.txt -o readme.txt
總結:會發現建立xml整個流程都很長,不適合放在函數中!公司去華爲的一個大神,先用雙向鏈表建立再返回,封裝一個函數,這樣就很方便了,我也在繼續優化中...