XML常見的操作

1. ? 創建XML文檔

(1)創建一個XML文檔非常簡單,其流程如下:

①??? 用xmlNewDoc函數創建一個文檔指針doc。

②??? 用xmlNewNode函數創建一個節點指針root_node。

③??? 用xmlDocSetRootElement將root_node設置為doc的根結點。

④??? 給root_node添加一系列的子節點,并設置子節點的內容和屬性。

⑤??? 用xmlSaveFile將XML文檔存入文件。

⑥??? 用xmlFreeDoc關閉文檔指針,并清除本文檔中所有節點動態申請的內存。

有多種方式可以添加子節點,如可以用xmlNewTextChild直接添加一個文本子節點。也可以先創建新節點,然后用xmlAddChild將新節點加入到上層節點中。

?

(2)創建xml文件舉例

CreateXmlFile.c源代碼如下:

#include <stdio.h>

#include <libxml/parser.h>

#include <libxml/tree.h>

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("CreateXml.xml",doc);

???? if (nRel != -1)

???? {

??????? printf("一個xml文檔被創建,寫入%d個字節\n", nRel);

???? }

???? //釋放文檔內節點動態申請的內存

???? xmlFreeDoc(doc);

???? return 1;

}

編譯 gcc CreateXmlFile.c -o CreateXmlFile -I/usr/local/include/libxml2?????? -lxml2。

執行./CreateXmlFile,會生成一個XML文件CreatedXml.xml。打開后如下所示:

<?xml version="1.0"?>

<root>

??? <newNode1>newNode1 content</newNode1>

??? <newNode2>newNode2 content</newNode2>

??? <newNode3>newNode3 content</newNode3>

??? <node2 attribute="yes">NODE CONTENT</node2>

??? <son>

?????? <grandson>This is a grandson node</grandson>

??? </son>

</root>

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

2. ? 解析XML文檔

(1)XML解析流程

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

①??? 用xmlReadFile函數讀入一個文件,并返回一個文檔指針doc。

②??? 用xmlDocGetRootElement函數得到根節點curNode。

③??? 此時curNode->xmlChildrenNode就是根節點的首個兒子節點,該兒子節點的兄弟節點可用next指針進行輪詢。

④??? 輪詢所有子節點,找到所需的節點,用xmlNodeGetContent取出其內容。

⑤??? 用xmlHasProp查找含有某個屬性的節點,屬性列表指針xmlAttrPtr將指向該節點的屬性列表。

⑥??? 取出該節點的屬性,用xmlGetProp取出其屬性值。

⑦??? xmlFreeDoc函數關閉文檔指針,并清除本文檔中所有節點動態申請的內存。

?

(2)XML解析舉例

ParseXmlFile.c源代碼如下:

#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/local/include/libxml2? -lxml2。

執行 ./ParseXmlFile CreateXml.xml,執行結果如下:

newNode1: newNode1 content

get attribute=yes

3. ? 修改XML文檔

有了上面的基礎,修改XML文檔的內容就簡單了。首先打開一個已經存在的XML文檔,順著根結點找到需要添加、刪除、修改的地方,調用相應的XML函數對節點進行增、刪、改操作。

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

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

?????? {

?????????? xmlNodePtr tempNode;

?????????? tempNode = curNode->next;

?????????? xmlUnlinkNode(curNode);

?????????? xmlFreeNode(curNode);

?????????? curNode = tempNode;

?????????? continue;

?????? }

此段代碼完成將當前節點從文檔中斷鏈(unlink),這樣此XML文檔就不會再包含這個節點,該節點斷鏈后需使用xmlFreeNode來釋放該節點申請的動態內存空間。

4. ? 使用XPath查找XML文檔

在libxml2中使用XPath非常簡單,其流程如下:

①??? 定義一個XPath上下文指針xmlXPathContextPtr context,并且使用xmlXPathNewContext函數來初始化這個指針。

②??? 定義一個XPath對象指針xmlXPathObjectPtr result,并且使用xmlXPathEvalExpression函數來計算XPath表達式,得到查詢結果,將結果存入對象指針中。

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

④??? 使用xmlXPathFreeContext釋放上下文指針。

⑤??? 使用xmlXPathFreeObject釋放XPath對象指針。

?

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

}

5. ? 用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)來釋放內存。

如果轉碼文件可以選擇系統調用來進行文件轉碼。下文中f表示from,t表示to,其轉碼方法如下:

system("iconv –f 源格式 –t 目標格式 源文件 >目標文件")

system("iconv –f? GB18030 –t UTF-8? test_gb.txt > test_utf.txt")

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/445080.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/445080.shtml
英文地址,請注明出處:http://en.pswp.cn/news/445080.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

算法(2)-二叉樹的遍歷(遞歸/迭代)python實現

二叉樹的遍歷1.深度優先DFS1.1 DFS 遞歸解法1.1.1先序遍歷1.1.2中序遍歷1.1.3后序遍歷1.2 DFS迭代解法1.2.1先序遍歷1.2.2中序遍歷1.2.3后序遍歷2.廣度優先BFS3.二叉樹的最大深度3.1遞歸3.2迭代4.翻轉二叉樹4.1遞歸4.1迭代5.合并兩棵二叉樹5.1遞歸5.2迭代有兩種通用的遍歷樹的策…

libxml的安裝和相關數據結構詳解

1安裝 一般如果在安裝系統的時候選中了libxml開發庫的話&#xff0c;系統會默認安裝。如果沒有安裝&#xff0c;可以按如下步驟進行手工安裝。 ① 從xmlsoft站點或ftp(ftp.xmlsoft.org)站點下載libxml壓縮包 (libxml2-xxxx.tar.gz) ② 對壓縮包進行解壓縮 tar xvzf …

內核中的 likely() 與 unlikely()

在 2.6 內核中&#xff0c;隨處可以見到 likely() 和 unlikely() 的身影&#xff0c;那么為什么要用它們&#xff1f;它們之間有什么區別&#xff1f; 首先要明確&#xff1a; if(likely(value)) 等價于 if(value) if(unlikely(value)) 也等價于 if(value) 也就是說 likely()…

python外卷(12)-sort(),sorted(),ord(),chr()

Python內置函數1.sort()&#xff0c;sorted()2.ord(), chr()1.sort()&#xff0c;sorted() sort() 是list的方法&#xff0c;對已經存在的列表進行操作&#xff0c;無返回值 a[3,2,4,1] b["c","a","b"] print (a.sort(),b.sort()) # 輸出 (Non…

利用posix_fadvise清理系統中的文件緩存

利用posix_fadvise清理系統中的文件緩存leoncom c/c,unix2011-08-03當我們需要對某段讀寫文件并進行處理的程序進行性能測試時&#xff0c;文件會被系統cache住從而影響I/O的效率&#xff0c;必須清理cache中的對應文件的才能正確的進行性能測試。通常清理內存可以采用下面的這…

空間分配

目前主流的垃圾收集器都會采用分代回收算法&#xff0c;因此需要將堆內存分為新生代和老年代&#xff0c;這樣我們就可以根據各個年代的特點選擇合適的垃圾收集算法。 大多數情況下&#xff0c;對象在新生代中 eden 區分配。當 eden 區沒有足夠空間進行分配時&#xff0c;虛擬…

關于uint32_t uint8_t uint64_t 的問題

怎么又是u又是_t的?u代表的是unsigned相信大家都知道,那么_t又是什么呢?我認為它就是一個結構的標注,可以理解為type/typedef的縮寫,表示它是通過typedef定義的,而不是其它數據類型。 uint8_t,uint16_t,uint32_t等都不是什么新的數據類型,它們只是使用typedef給類型起…

學點數學(4)-協方差矩陣

協方差矩陣協方差矩陣&#xff08;從隨機變量講起&#xff09;隨機變量x&#xff1a;表示隨機試驗各種結果的 實值 單值函數&#xff0c;就是說隨機變量x是一個函數映射&#xff0c;其取值為標量。隨機變量有離散型和連續型&#xff0c;離散型&#xff1a;拋10次硬幣&#xff…

RedLock

概念 Redis 官方站這篇文章提出了一種權威的基于 Redis 實現分布式鎖的方式名叫 Redlock&#xff0c;此種方式比原先的單節點的方法更安全。它可以保證以下特性&#xff1a; 安全特性&#xff1a;互斥訪問&#xff0c;即永遠只有一個 client 能拿到鎖避免死鎖&#xff1a;最終…

GCC中常用的優化的參數

-pipe 的作用&#xff1a; 使用管道代替編譯中臨時文件&#xff0c; -pipe 加速編譯 gcc -pipe foo.c -o foo 加速 在將源代碼變成可執行文件的過程中,需要經過許多中間步驟,包含預處理、編譯、匯編和連接。這些過程實際上是由不同的程序負責完成的。大多數情況下 GCC 可以為 …

Linux與時間相關的結構體及相關用法

1. Linux下與時間有關的結構體 struct timeval { int tv_sec; int tv_usec; }; 其中tv_sec是由凌晨開始算起的秒數&#xff0c;tv_usec則是微秒(10E-6 second)。 struct timezone { …

算法(3)-數據結構-數組和字符串

leetcode-explore-learn-數據結構-數據結構-數組和字符串1. 一維數組1.0 概況1.1 尋找數組的中心索引1.2 搜索插入位置1.3 合并區間1.4 至少是其他數字兩倍大的最大數1.5 加一2. 二維數組2.1旋轉矩陣本系列博文為leetcode-explore-learn子欄目學習筆記&#xff0c;如有不詳之處…

redis的入門/原理/實戰大總結

入門 Redis是一款基于鍵值對的NoSQL數據庫&#xff0c;它的值支持多種數據結構&#xff1a; 字符串(strings)、哈希(hashes)、列表(lists)、集合(sets)、有序集合(sorted sets)等。 ? Redis將所有的數據都存放在內存中&#xff0c;所以它的讀寫性能十分驚人&#xff0c;用作數…

創建與打開IPC通道的POSIX和SYSTEM V方法

先說&#xff30;&#xff2f;&#xff33;&#xff29;&#xff38;的吧&#xff1a; mq_open&#xff0c;sem_open&#xff0c;shm_open著三個函數用于創建或者打開一個IPC通道。 由此可見&#xff0c;消息隊列的讀寫權限是任意的&#xff0c;然而信號燈就沒有&#xff0c;…

算法(4)-leetcode-explore-learn-數據結構-數組2

leetcode-explore-learn-數據結構-數組21.簡述2.例題2.1 二維數組的對角線遍歷2.2 螺旋遍歷2.3 楊輝三角本系列博文為leetcode-explore-learn子欄目學習筆記&#xff0c;如有不詳之處&#xff0c;請參考leetcode官網&#xff1a;https://leetcode-cn.com/explore/learn/card/ar…

軟件測試基礎知識

第一章 1.1 軟件測試背景知識和發展史 互聯網公司職位架構&#xff1a;產品 運營 技術 市場 行政軟件測試&#xff1a;使用人工或自動化手段&#xff0c;來運行或測試某個系統的過程&#xff0c;其目的在于檢驗它是否滿足規定的需求或弄清預期結果與實際結果之間的差別&#…

key_t IPC鍵和ftok函數詳解和剖析

統建立IPC通訊&#xff08;如消息隊列、共享內存時&#xff09;必須指定一個ID值。通常情況下&#xff0c;該id值通過ftok函數得到。 ftok原型如下&#xff1a; key_t ftok( char * fname, int id ) fname就時你指定的文件名(該文件必須是存在而且可以訪問的)&#xff0c;id是子…

算法(5)-leetcode-explore-learn-數據結構-字符串

leetcode-explore-learn-數據結構-數組3-字符串1.簡述2.例題2.1 二進制求和2.2實現strStr()2.3最長公共前綴本系列博文為leetcode-explore-learn子欄目學習筆記&#xff0c;如有不詳之處&#xff0c;請參考leetcode官網&#xff1a;https://leetcode-cn.com/explore/learn/card…

ipcs命令查看管道,消息隊列,共享內存

修改消息隊列大小&#xff1a; root&#xff1a;用戶&#xff1a; /etc/sysctl.conf kernel.msgmnb 4203520 #kernel.msgmnb 3520 kernel.msgmni 2878 保存后需要執行 sysctl -p ,然后重建所有消息隊列 ipcs -q : 顯示所有的消息隊列 ipcs -qt : 顯示消息隊列的創建時…

Jmeter-基礎篇

常用壓力測試工具對比 1、loadrunner 性能穩定&#xff0c;壓測結果及細粒度大&#xff0c;可以自定義腳本進行壓測&#xff0c;但是太過于重大&#xff0c;功能比較繁多 2、apache ab(單接口壓測最方便) 模擬多線程并發請求,ab命令對發出負載的計算機…