1、前言

  xml广泛应用于网络数据交换,配置文件、Web服务等等。近段时间项目中做一些配置文件,原来是用ini,现在改用xml。xml相对来说可视性更为直观,很容易看出数据之间的层次关系。关于xml的详细介绍可以参考http://baike.baidu.com/view/159832.htm?fromId=63。本文重点介绍解析xml的libxml2库的安装及使用,举例说明创建和解析xml的过程。

2、libxml2的安装

  关于libxml2的介绍请参考官方网址http://xmlsoft.org/,下载最新的libxml2库http://xmlsoft.org/downloads.html

具体安装步骤:
1、解压:$tar zxvf  libxml2-2.9.1.tar.gz
2、进入解压后的安装目录:$cd  libxml2-2.9.1
3、安装三部曲:

     1)$./configure
              2)$make
              3)$make install
安装完毕。

注意:libxml2默认安装到/usr/local/include/libxml2目录下

3、xml文档结构

  xml按照树形结构进行存储,节点分为元素和文本,必须有根节点。如下的xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<phone_books>
<phone id="1">
<name>Anker</name>
<tel>18923873456</tel>
<address>Shenzheng</address>
</phone>
<phone id="2">
<name>Jermey</name>
<tel>18623873456</tel>
<address>Beijing</address>
</phone>
<phone id="3">
<name>Lili</name>
<tel>13223873456</tel>
<address>Shanghai</address>
</phone>
</phone_books>

xml结构图如下所示:

4、测试例子

关于libxml2的提供的接口,可以参考http://blog.csdn.net/shanzhizi/article/details/7726679。libxml2常用的接口如下:

  内部字符类型:xmlChar,定义为:typedef unsigned char xmlChar,用无符号型的char方便表示utf-8编码。libxml2提供了一个宏进行转换,#define BAD_CAST (xmlChar *)。

文档类型xmlDoc,指针类型xmlDocPtr。xmlDoc是个struct,保存了一个xml的相关信息,例如文件名、文件类型、子节点等等;xmlDocPtr等于xmlDoc*。

  xmlNewDoc函数创建一个新的文件指针。

  xmlParseFile函数以默认方式读入一个UTF-8格式的文件,并返回文件指针。

  xmlReadFile函数读入一个带有某种编码的xml文件,并返回文件指针;细节见libxml2参考手册。

  xmlFreeDoc释放文件指针。特别注意,当你调用xmlFreeDoc时,该文件所有包含的节点内存都被释放。 

  xmlFreeNodeList来释放动态分配的节点内存,除非你把该节点从文件中移除了。

xmlSaveFile将文件以默认方式存入一个文件。

xmlSaveFormatFileEnc可将文件以某种编码/格式存入一个文件中。

  节点类型xmlNode、指针xmlNodePtr

  节点应该是xml中最重要的元素了,xmlNode代表了xml文件中的一个节点,实现为一个struct,内容非常丰富:tree.h

  xmlDocSetRootElement函数能将一个节点设置为某个文件的根节点

xmlNewNode函数创建一个节点指针root_node

(1)创建xml文件

测试程序如下所示:

 /****************************************
*练习libxml库,创建通讯录xml文档,新增一个通讯录,
*如果xml文件存在,则添加一个新节点
*如果xml文件不存在,则新建一个xml文件
*
*@author: Anker @date: 2014/02/09
* ***************************************/ #include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <assert.h> #include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxml/xmlmemory.h> #define PHONE_BOOK_FILE "phone_book.xml"
#define ID_STR_LEN 16
#define NAME_STR_LEN 32
#define TEL_STR_LEN 16
#define ADDR_STR_LEN 128 //电话通讯录结构体
typedef struct phone_t {
int id; //编号
char name[NAME_STR_LEN]; //姓名
char tel[TEL_STR_LEN]; //电话
char address[ADDR_STR_LEN]; //地址
}phone; //设置通讯录项
static void set_phone_item(phone *phone_item)
{
assert(phone_item); phone_item->id = ;
snprintf(phone_item->name, NAME_STR_LEN, "%s", "Anker");
snprintf(phone_item->tel, TEL_STR_LEN, "%s", "");
snprintf(phone_item->address, ADDR_STR_LEN, "%s", "Shenzheng");
} //创建phone节点
static xmlNodePtr create_phone_node(const phone *phone_item)
{
assert(phone_item); char id[ID_STR_LEN] = {};
xmlNodePtr phone_node = NULL; phone_node = xmlNewNode(NULL, BAD_CAST"phone");
if (phone_node == NULL) {
fprintf(stderr, "Failed to create new node.\n");
return NULL;
}
//设置属性
snprintf(id, ID_STR_LEN, "%d", phone_item->id);
xmlNewProp(phone_node, BAD_CAST"id", (xmlChar*)id); xmlNewChild(phone_node, NULL, BAD_CAST"name", (xmlChar *)phone_item->name);
xmlNewChild(phone_node, NULL, BAD_CAST"tel", (xmlChar *)phone_item->tel);
xmlNewChild(phone_node, NULL, BAD_CAST"address", (xmlChar *)phone_item->address); return phone_node;
} //向根节点中添加一个phone节点
static int add_phone_node_to_root(xmlNodePtr root_node)
{
xmlNodePtr phone_node = NULL;
phone *phone_item = NULL; //创建一个新的phone
phone_item = (phone *)malloc(sizeof(phone));
if (phone_item == NULL) {
fprintf(stderr, "Failed to malloc memory.\n");
return -;
}
set_phone_item(phone_item); //创建一个phone节点
phone_node = create_phone_node(phone_item);
if (phone_node == NULL) {
fprintf(stderr, "Failed to create phone node.\n");
goto FAILED;
}
//根节点添加一个子节点
xmlAddChild(root_node, phone_node);
free(phone_item); return ;
FAILED:
if (phone_item){
free(phone_item);
}
return -;
} //创建phone_books
static int create_phone_books(const char *phone_book_file)
{
assert(phone_book_file); xmlDocPtr doc = NULL;
xmlNodePtr root_node = NULL; //创建一个xml 文档
doc = xmlNewDoc(BAD_CAST"1.0");
if (doc == NULL) {
fprintf(stderr, "Failed to new doc.\n");
return -;
} //创建根节点
root_node = xmlNewNode(NULL, BAD_CAST"phone_books");
if (root_node == NULL) {
fprintf(stderr, "Failed to new root node.\n");
goto FAILED;
}
//将根节点添加到文档中
xmlDocSetRootElement(doc, root_node); if (add_phone_node_to_root(root_node) != ) {
fprintf(stderr, "Failed to add a new phone node.\n");
goto FAILED;
}
//将文档保存到文件中,按照utf-8编码格式保存
xmlSaveFormatFileEnc(phone_book_file, doc, "UTF-8", );
//xmlSaveFile("test.xml", doc);
xmlFreeDoc(doc); return ;
FAILED:
if (doc) {
xmlFreeDoc(doc);
} return -;
} static int add_phone_node(const char *phone_book_file)
{
assert(phone_book_file); xmlDocPtr doc = NULL;
xmlNodePtr root_node = NULL;
xmlNodePtr phone_node = NULL;
phone *phone_item = NULL; doc = xmlParseFile(phone_book_file);
if (doc == NULL) {
fprintf(stderr, "Failed to parser xml file:%s\n", phone_book_file);
return -;
} root_node = xmlDocGetRootElement(doc);
if (root_node == NULL) {
fprintf(stderr, "Failed to get root node.\n");
goto FAILED;
} if (add_phone_node_to_root(root_node) != ) {
fprintf(stderr, "Failed to add a new phone node.\n");
goto FAILED;
}
//将文档保存到文件中,按照utf-8编码格式保存
xmlSaveFormatFileEnc(phone_book_file, doc, "UTF-8", );
xmlFreeDoc(doc); return ;
FAILED:
if (doc) {
xmlFreeDoc(doc);
} return -;
} int main(int argc, char *argv[])
{
char *phone_book_file = PHONE_BOOK_FILE; if (argc == ) {
phone_book_file = argv[];
} if (access(phone_book_file, F_OK) == ) {
//文件存在,添加一个新的phone节点
add_phone_node(phone_book_file);
}
else {
//文件不存在,创建一个信息的phone book
create_phone_books(phone_book_file);
} return ;
}

编译命令如下:gcc -g create_phone_book.c -o create_phone_book -I /usr/local/include/libxml2/ -lxml2

执行结果如下图所示:

(2)解析xml文档

测试程序如下所示:

 /************************************
* 调用libxml2库解析xml,提取出电话薄信息
*
* @author:Anker @date:2014/02/09
* *********************************/ #include <stdio.h>
#include <assert.h> #include <libxml/xmlmemory.h>
#include <libxml/parser.h> #define DEFAULT_XML_FILE "phone_book.xml" //解析每一个phone,提取出name、tel、address
static int parse_phone(xmlDocPtr doc, xmlNodePtr cur)
{
assert(doc || cur);
xmlChar *key; cur = cur->xmlChildrenNode;
while (cur != NULL) {
//获取name
if ((!xmlStrcmp(cur->name, (const xmlChar *)"name"))) {
key = xmlNodeListGetString(doc, cur->xmlChildrenNode, );
printf("name: %s\t", key);
xmlFree(key);
}
//获取tel
if ((!xmlStrcmp(cur->name, (const xmlChar *)"tel"))) {
key = xmlNodeListGetString(doc, cur->xmlChildrenNode, );
printf("tel: %s\t", key);
xmlFree(key);
}
//获取address
if ((!xmlStrcmp(cur->name, (const xmlChar *)"address"))) {
key = xmlNodeListGetString(doc, cur->xmlChildrenNode, );
printf("address: %s\n", key);
xmlFree(key);
}
cur = cur->next;
}
return ;
} static int parse_phone_book(const char *file_name)
{
assert(file_name); xmlDocPtr doc; //xml整个文档的树形结构
xmlNodePtr cur; //xml节点
xmlChar *id; //phone id //获取树形结构
doc = xmlParseFile(file_name);
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 *)"phone_books"))) {
fprintf(stderr, "The root is not phone_books.\n");
goto FAILED;
} //遍历处理根节点的每一个子节点
cur = cur->xmlChildrenNode;
while (cur != NULL) {
if ((!xmlStrcmp(cur->name, (const xmlChar *)"phone"))) {
id = xmlGetProp(cur, "id");
printf("id:%s\t",id);
parse_phone(doc, cur);
}
cur = cur->next;
}
xmlFreeDoc(doc);
return ;
FAILED:
if (doc) {
xmlFreeDoc(doc);
}
return -;
} int main(int argc, char*argv[])
{
char *xml_file = DEFAULT_XML_FILE; if (argc == ) {
xml_file = argv[];
} if (parse_phone_book(xml_file) != ) {
fprintf(stderr, "Failed to parse phone book.\n");
return -;
} return ;
}

测试结果如下所示:

最新文章

  1. 一个ubuntu phper的自我修养(lamp)
  2. myeclipse9中导入的jquery文件报错(出现红叉叉,提示语法错误)
  3. c# 注册全局热键
  4. Create XO Checker Game With Oracle Forms
  5. 【BZOJ1008】【HNOI2008】越狱
  6. JavaScript中对于闭包的理解
  7. linux动态库默认搜索路径设置的三种方法
  8. 通用php与mysql数据库配置文件
  9. 谈谈Ext JS的组件——布局的用法
  10. Subpub 订阅/发布
  11. SQL Server 行转列,列转行。多行转成一列
  12. OS + CentOS cmake
  13. PostgreSQL学习笔记(二)-安装pgAdmin
  14. python爬虫 | 一条高效的学习路径
  15. 根据cid获取哔哩哔哩弹幕
  16. [Spark][Python]Spark 访问 mysql , 生成 dataframe 的例子:
  17. 转 $(document).ready()与window.onload的区别
  18. PS学习之小猪佩奇身上纹,掌声送给社会人
  19. python3.4学习笔记(九) Python GUI桌面应用开发工具选择
  20. OpenGL中的旋转是可以叠加的?

热门文章

  1. hdu 5773 The All-purpose Zero 线段树 dp
  2. spring boot 集成 druid
  3. CentOS 7下KVM支持虚拟化/嵌套虚拟化配置
  4. Redis "MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk"问题的解决(转)
  5. Unity3D实践系列11, 组件的添加和访问
  6. INDY10的IDHttpServer应答客户端
  7. 两个Activity之间共享数据、互相访问的另一种方式的实现
  8. 较老版本 AFNetworking 使用心得
  9. 深入分析ReentrantLock公平锁和非公平锁的区别
  10. 虚拟机内存复用技术的比较(XEN系统)