目錄
libxml是一個用於解析xml檔案的庫,在各個平臺下都能使用,也支援多種語言,如c,Python等。
sudo apt-get install libxml2
sudo apt-get install libxml2-dev
先在官網下載原始碼,然後執行原始碼安裝三部曲
1)$./configure
2)$make
過程可能稍微有點漫長。耐心等待一下。
3)$sudo make install
一個函數庫中可能有幾百種數據型別及幾千個函數,不過記住大師的話,90%的功能都是由30%的內容提供的。對於libxml2搞懂以下的數據型別和函數就足以了。
1.1 內部字元型別xmlChar
xmlChar是Libxml2中的字元型別,庫中所有字元、字串都是基於這個數據型別。事實上他的定義是:xmlstring.h
typedef unsigned char xmlChar
使用unsigned char作爲內部字元格式是考慮到他能非常好適應UTF-8編碼,而UTF-8編碼正是libxml2的內部編碼,其他格式的編碼要轉換爲這個編碼才能 纔能在libxml2中使用。還經常能看到使用xmlChar*作爲字串型別,非常多函數會返回一個動態分配記憶體的xmlChar*變數,使用這樣的函數時記得要手動刪除記憶體。
1.2 xmlChar相關函數
如同標準c中的char型別相同,xmlChar也有動態記憶體分配、字串操作等相關函數。例如xmlMalloc是動態分配記憶體的函數;xmlFree是配套的釋放記憶體函數;xmlStrcmp是字串比較函數等等。
基本上xmlChar字串相關函數都在xmlstring.h中定義;而動態記憶體分配函數在xmlmemory.h中定義。
1.3 xmlChar*和其他型別之間的轉換
另外要注意,因爲總是要在xmlChar*和char*之間進行型別轉換,所以定義了一個宏BAD_CAST,其定義如下:xmlstring.h
#define BAD_CAST (xmlChar *)
原則上來說,unsigned char和char之間進行強制型別轉換是沒有問題的。
1.4 檔案型別xmlDoc、指針xmlDocPtr
xmlDoc是個struct,儲存了一個xml的相關資訊,例如檔名、檔案型別、子節點等等;xmlDocPtr等於xmlDoc*,他搞成這個樣子總讓人以爲是智慧指針,其實不是,要手動刪除的。
xmlNewDoc()函數建立一個新的檔案指針。
xmlParseFile()函數以預設方式讀入一個UTF-8格式的檔案,並返迴檔案指針。
xmlReadFile()函數讀入一個帶有某種編碼的xml檔案,並返迴檔案指針;細節見libxml2參考手冊。
xmlFreeDoc()釋放檔案指針。特別注意,當你呼叫xmlFreeDoc時,該檔案所有包含的節點記憶體都被釋放,所以一般來說不必手動
xmlFreeNode或xmlFreeNodeList來釋放動態分配的節點記憶體,除非你把該節點從檔案中移除了。一般來說,一個檔案中所有節點都應該動態分配,然後加入檔案,最後呼叫xmlFreeDoc一次釋放所有節點申請的動態記憶體,這也是爲什麼我們非常少看見xmlNodeFree的原因。
xmlSaveFile()將檔案以預設方式存入一個檔案。
xmlSaveFormatFileEnc()可將檔案以某種編碼/格式存入一個檔案中。
1.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能組織爲樹。同時更有以下重要元素:
節點中的文字內容:content;
節點所屬檔案:doc;
節點名字:name;
節點的namespace:ns;
節點屬性列表:properties;
Xml檔案的操作其根本原理就是在節點之間移動、查詢節點的各項資訊,並進行增加、刪除、修改的操作。
xmlDocSetRootElement函數能將一個節點設定爲某個檔案的根節點,這是將檔案和節點連線起來的重要手段,當有了根結點以後,所有子節點就能依次連線上根節點,從而組織成爲一個xml樹。
1.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來表示一個節點集合更好,而且沒有記憶體漏失或溢位的擔憂。
/*********************************************************************************
* Copyright: ysn
*
* Filename: query_apn.c
* Description: This file is used query APN by MCC/MNC in etc/apns-full-conf.xml,
* which is download from google Android:
* https://android.googlesource.com/device/sample/+/master/etc/apns-full-conf.xml
*
* Version: 1.0.0(2020年07月25日)
* Author: Tian Jincheng <[email protected]>
* ChangeLog: 1, Release initial version on "2020年08月12日 20時04分38秒"
*
********************************************************************************/
#include <libxml2/libxml/xmlmemory.h>
#include <libxml2/libxml/parser.h>
#include <libxml/xmlversion.h>
#include <string.h>
#include "query_apn.h"
#define FILE_NAME "/home/pi/program/etc/apns-full-conf.xml"
int parse_apn(const char *file_name, apn_info_t *apn_info);
int main (int argc, char **argv)
{
apn_info_t apn_info;
parse_apn(FILE_NAME, &apn_info);
return 0;
} /* ----- End of main() ----- */
int parse_apn(const char *file_name, apn_info_t *apn_info)
{
xmlDocPtr doc; /* xml document tree */
xmlNodePtr cur; /* xml node */
xmlChar *mcc; /* Operator MCC */
xmlChar *mnc; /* Operator MNC */
xmlChar *apn; /* pppd APN */
xmlChar *uid; /* APN user ID */
xmlChar *pwd; /* APN password */
xmlChar *version; /* APN list version */
char *qmcc = "460";
char *qmnc = "03";
int found = 0;
if( !file_name || !apn_info )
{
printf("Invalid input arguments\n");
}
doc = xmlReadFile(file_name, "UTF_8", XML_PARSE_RECOVER); //先讀入需要解析的xml檔案
if (doc == NULL)
{
fprintf(stderr, "Failed to parse xml file:%s\n", file_name);
goto FAILED;
}
cur = xmlDocGetRootElement(doc); //獲取根節點
if (cur == NULL)
{
fprintf(stderr, "Root is empty.\n");
goto FAILED;
}
if ( xmlStrcmp(cur->name, (const xmlChar *)"apns") )
{
fprintf(stderr, "The root is not apns.\n");
goto FAILED;
}
if (xmlHasProp(cur, "version"))
{
version = xmlGetProp(cur, "version");
printf("version: %s\n", version);
}
cur = cur->xmlChildrenNode;
while (cur != NULL)
{
if ( !xmlStrcmp(cur->name, (const xmlChar *)"apn"))
{
mcc = xmlGetProp(cur, "mcc");
mnc = xmlGetProp(cur, "mnc");
if( !xmlStrcmp(mcc, (const xmlChar *)qmcc) && !xmlStrcmp(mnc, (const xmlChar *)qmnc))
{
apn = xmlGetProp(cur, "apn");
uid = xmlGetProp(cur, "user");
pwd = xmlGetProp(cur, "password");
printf("mcc:%s mnc:%s apn:%s uid:%s pwd:%s\n",mcc, mnc, apn, uid, pwd);
if( !xmlStrstr(apn, "wap") )
{
found = 1;
}
}
}
cur = cur->next;
}
FAILED:
if (doc)
{
xmlFreeDoc(doc);
}
return found;
}