libxml是一個用於解析xml文件的庫,在各個平台下都能使用,也支持多種語言,如c,Python等。這里是官方網站。上面有libxml的api和一些code examples,都是英文的。不過比較簡單。
libxml的基礎功能就是對xml的讀和寫。下面將簡單介紹libxml的讀的功能。(大部分內容是參照libxml tutorial 的文檔)
0 編譯程序
因為本人是在Linux下用C語言來介紹libxml的,所以使用了gcc編譯器。其他的編譯命令請參照官網。
我們知道,gcc 最簡單的編譯命令為gcc filename.c 。只要給這個命令添加頭文件和鏈接庫的地址就可以使用libxml。而libxml也提供了一個腳本xml2-config來配置這些地址。所以編譯時將命令改為gcc filename.c `xml2-config --cflags --libs`即可。cflags在編譯時提供頭文件的地址,而libs在鏈接時提供庫文件的地址。
1 分析文件--parse file
先簡單介紹xml文件。
<?xml version="1.0" encoding="UTF-8"?>
<root>
<node1>content1</node1>
<node2 attribute="yes">content2</node2>
<node3>
<subnode>go</subnode>
</node3>
</root>
上面是一個簡單的xml文件。從文件中很容易就能看出整個文件的結構和要表達的意思。下面我會結合xml的基本類型介紹這個文件。
文件的第一行是xml文件的一些屬性,可以看出編碼方式是utf-8 。libxml只能處理uft-8 和 utf-16編碼的文件,如果你的文件不是這兩種編碼,需要在使用前進行轉換。
<root></root>是xml的一個節點,即xmlNode。而xmlNodePtr 表示指向xmlNode的指針--xmlNode*。<node1></node1>,<node2></node2>,<node3></node3>是這個node的子node,即xmlNodePtr->children或xmlNodePtr->xmlChildrenNode。所以<subnode></subnode>就是<node3></node3>的子node。
接下來就是解析文件了。
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- #include <libxml/tree.h>
- #include <libxml/parser.h>
文件中需要包含這兩個頭文件。
- xmlDocPtr doc = NULL;
- xmlNodePtr node = NULL;
- doc = xmlParseFile(filename);
- if(NULL == doc) {
- fprintf(stderr, "parse error\n");
- exit(1);
- }
xmlDocPtr 表示指向xmlDoc的指針。從tutorial中我們可以知道,xmlDoc是一個包含了已解析的文件生成的節點樹的結構體。
- node = xmlDocGetRootElement(doc);
- if(NULL == node) {
- fprintf(stderr, "doc has no content\n");
- xmlFreeDoc(doc);
- exit(1);
- }
xmlDocGetRootElement(doc)可以得到整個文件的根節點,所有的操作都是從根節點開始的。
現在我們已經有了一個包含這個節點樹的結構體指針xmlDocPtr doc,有了一個可以操作節點的結構體指針xmlNodePtr node,我們就可以讀取各個節點的信息了。
節點包含這么幾個信息:
node->name:節點的名字,如node1,node2,subnode等
node->xmlChildrenNode:節點的子節點
node->last:節點的最后一個子節點
node->parent:節點的父節點
node->next:節點的兄弟節點,對於node1來說,node2和node3都是其兄弟節點,node1的next指向node2
由於節點的內容是其子節點(對於node1,content1可以說是其子節點),所以我們需要使用xmlNodeGetContent來取出內容。
- node = node->xmlChildrenNode;
- while(node != NULL) {
- printf("name=%s content=%s\n",
- node->name,
- (char*)xmlNodeGetContent(node));
- node=node->next;
- }
這是完整的源碼。
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- #include <libxml/parser.h>
- static int
- parseDoc(char* docname) {
- xmlDocPtr doc;
- xmlNodePtr cur;
- //xmlKeepBlanksDefault(0);
- doc = xmlParseFile(docname);
- if(doc == NULL) {
- fprintf(stderr, "doc error!\n");
- return 0;
- }
- cur = xmlDocGetRootElement(doc);
- if(cur == NULL) {
- fprintf(stderr, "root error!\n");
- xmlFreeDoc(doc);
- return 0;
- }
- if(xmlStrcmp(cur->name, (const xmlChar*)"root")) {
- printf("end\n");
- return 0;
- }
- cur = cur->children;
- while(cur != NULL) {
- printf("name=%s content=%s\n",cur->name,
- (char*)xmlNodeGetContent(cur));
- //cur->content);
- cur = cur->next;
- }
- xmlFreeDoc(doc);
- return 0;
- }
- int main() {
- char* docname = "story.xml";
- parseDoc(docname);
- return 1;
- }
編譯鏈接后執行,會發現輸出的結果如下:
- name=text content=
- name=node1 content=content1
- name=text content=
- name=node2 content=content2
- name=text content=
- name=node3 content=
- go
- name=text content=
這是因為libxml默認將各個節點間的空格當作一個節點,只要在調用xmlParseFile之前調用xmlKeepBlanksDefault(0)即可。
修改后結果如下:
- name=node1 content=content1
- name=node2 content=content2
- name=node3 content=go
2 讀取內存中的xml文檔。
很多時候我們需要對內存中的xml文檔進行操作。比如在網絡編程的時候經常會從server那里接受一些客戶端的配置信息,這些配置信息大部分都是使用xml語言描述的。在你將這些信息讀入到buffer中后,你無需將他們寫入文件當中再分析,直接可以調用xmlReadMemory函數就可以得到一個xmlDocPtr。這個函數接受五個參數,第一個參數為緩存區的指針,第二個參數為緩存區大小,其他參數具體看API文檔。當然另外一個函數xmlReadDoc也能實現這樣的功能。
- static char* config = "<config />";
- xmlDocPtr doc = NULL;
- xmlKeepBlanksDefault(0);
- doc = xmlReadMemory(config, strlen(config), "in_memory.xml", NULL, 0);
- //handle doc
- xmlFreeDoc(doc);
- 頂
- 0