https://blog.csdn.net/chengwenyao18/article/details/7176082

一、关于XML:

在开始研究 Libxml2 库之前,先了解一下XML的相关基础。XML 是一种基于文本的格式,它可用来创建能够通过各种语言和平台访问的结构化数据。它包括一系列类似 HTML 的标记,并以树型结构来对这些标记进行排列。

例如,可参见清单 1 中介绍的简单文档。为了更清楚地显示 XML 的一般概念,下面是一个简化的XML文件。

清单 1. 一个简单的 XML 文件

<?xml version="1.0" encoding="UTF-8"?>

<files>

<owner>root</owner>

<action>delete</action>

<age units="days">10</age>

</files>

清单 1 中的第一行是 XML 声明,它告诉负责处理 XML 的应用程序,即解析器,将要处理的 XML 的版本。大部分的文件使用版本 1.0 编写,但也有少量的版本 1.1 的文件。它还定义了所使用的编码。大部分文件使用 UTF-8,但是,XML 设计用来集成各种语言中的数据,包括那些不使用英语字母的语言。

接下来出现的是元素。一个元素以开始标记 开始(如 <files>),并以结束标记 结束(如 </files>),其中使用斜线 (/) 来区别于开始标记。元素是 Node 的一种类型。XML 文档对象模型 (DOM) 定义了几种不同的 Nodes 类型,包括:

Elements(如 files 或者 age)

Attributes(如 units)

Text(如 root 或者 10)

元素可以具有子节点。例如,age 元素有一个子元素,即文本节点 10。

二、使用Libxml2

项目中要实现一个管理XML文件的后台程序,需要对XML文件进行创建,解析,修改,查找等操作,下面介绍如何利用libxml2提供的库来实现上述功能。

1、创建XML文档:

我们使用xmlNewDoc()来创建XML文档,然后使用xmlNewNode(),xmlNewChild(),xmlNewProp(),xmlNewText()等函数向XML文件中添加节点及子节点,设置元素和属性,创建完毕后用xmlSaveFormatFileEnc()来保存XML文件到磁盘(该函数可以设置保存XML文件时的编码格式)。

示例1:

#include <stdio.h>

#include <libxml/parser.h>

#include <libxml/tree.h>

int main(int argc, char **argv)

{

xmlDocPtr doc = NULL;       //文件指针

xmlNodePtr root_node = NULL, node = NULL, node1 = NULL;//节点指针

// 创建一个文件,以及设置一个根节点

doc = xmlNewDoc(BAD_CAST "1.0");

root_node = xmlNewNode(NULL, BAD_CAST "root");

xmlDocSetRootElement(doc, root_node);

//创建一个绑定在根节点的子节点

xmlNewChild(root_node, NULL, BAD_CAST "node1",BAD_CAST "content of node1");

//通过xmlNewProp()增加一个节点的属性

node=xmlNewChild(root_node, NULL, BAD_CAST "node3", BAD_CAST"node has attributes");

xmlNewProp(node, BAD_CAST "attribute", BAD_CAST "yes");

//创建节点的另一种方法

node = xmlNewNode(NULL, BAD_CAST "node4");

node1 = xmlNewText(BAD_CAST"other way to create content");

xmlAddChild(node, node1);

xmlAddChild(root_node, node);

//保存文件

xmlSaveFormatFileEnc(argc > 1 ? argv[1] : "-", doc, "UTF-8", 1);

/*free the document */

xmlFreeDoc(doc);

xmlCleanupParser();

xmlMemoryDump();//debug memory for regression tests

return(0);

}

2、解析XML文档

解析文档时仅仅需要文件名并只调用一个函数,并有错误检查,常用的相关函数有xmlParseFile(),xmlParseDoc(),获取文档指针后,就可以使用xmlDocGetRootElement()来获取根元素节点指针,利用该指针就可以在DOM树里漫游了,结束后要调用xmlFreeDoc()释放。

示例2:

xmlDocPtr doc;   //定义解析文档指针

xmlNodePtr cur;  //定义结点指针(你需要它为了在各个结点间移动)

xmlChar *key;

doc = xmlReadFile(url, MY_ENCODING, 256);  //解析文件

/*检查解析文档是否成功,如果不成功,libxml将指一个注册的错误并停止。一个常见错误是不适当的编码。XML标准文档除了用UTF-8或UTF-16外还可用其它编码保存。如果文档是这样,libxml将自动地为你转换到UTF-8。更多关于XML编码信息包含在XML标准中。*/

if (doc == NULL )

{

fprintf(stderr,"Document not parsed successfully. \n");

return;

}

cur = xmlDocGetRootElement(doc);  //确定文档根元素

/*检查确认当前文档中包含内容*/

if (cur == NULL)

{

fprintf(stderr,"empty document\n");

xmlFreeDoc(doc);

return;

}

/*在这个例子中,我们需要确认文档是正确的类型。“root”是在这个示例中使用文档的根类型。*/

if (xmlStrcmp(cur->name, (const xmlChar *) "root"))

{

fprintf(stderr,"document of the wrong type, root node != root");

xmlFreeDoc(doc);

return;

}

cur = cur->xmlChildrenNode;

while(cur!=NULL)

{

if ((!xmlStrcmp(cur->name, (const xmlChar *)"keyword")))

{

key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);

printf("keyword: %s\n", key);

xmlFree(key);

}

cur = cur->next;

}

xmlFreeDoc(doc);

3、修改XML元素及属性等信息

要修改XML文档里的元素及属性等信息,先需要解析XML文档,获得一个节点指针(xmlNodePtr node),利用该节点指针漫游DOM树,就可以在XML文档中获取,修改,添加相关信息。

示例3:

//得到一个节点的内容:

xmlChar *value = xmlNodeGetContent(node);

//返回值value应该使用xmlFree(value)释放内存

//得到一个节点的某属性值:

xmlChar *value = xmlGetProp(node, (const xmlChar *)"prop1");

//返回值需要xmlFree(value)释放内存

//设置一个节点的内容:

xmlNodeSetContent(node, (const xmlChar *)"test");

//设置一个节点的某属性值:

xmlSetProp(node, (const xmlChar *)"prop1", (const xmlChar *)"v1");

//添加一个节点元素:

xmlNewTextChild(node, NULL, (const xmlChar *)"keyword", (const xmlChar *)"test Element");

//添加一个节点属性:

xmlNewProp(node, (const xmlChar *)"prop1", (const xmlChar *)"test Prop");

4、查找XML节点

有时候对一个XML文档我们可能只关心其中某一个或某几个特定的Element的值或其属性,如果漫游DOM树将是很痛苦也很无聊的事,利用XPath可以非常方便地得到你想的Element。下面是一个自定义函数:

示例4:

xmlXPathObjectPtr get_nodeset(xmlDocPtr doc, const xmlChar *xpath)

{

xmlXPathContextPtr context;

xmlXPathObjectPtr result;

context = xmlXPathNewContext(doc);

if (context == NULL)

{

printf("context is NULL\n");

return NULL;

}

result = xmlXPathEvalExpression(xpath, context);

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;

}

在doc指向的XML文档中查询满足xpath表达式条件的节点,返回满足这一条件的节点集合查询条件xpath的写法参见xpath相关资料。在查询完毕获取结果集后,就可以通过返回的 xmlXPathObjectPtr 结构访问该节点:

示例5:

xmlChar *xpath = ("/root/node/[@key='keyword']");

xmlXPathObjectPtr app_result = get_nodeset(doc,xpath);

if (app_result == NULL)

{

printf("app_result is NULL\n");

return;

}

int i = 0;

xmlChar *value;

if(app_result)

{

xmlNodeSetPtr nodeset = app_result->nodesetval;

for (i=0; i < nodeset->nodeNr; i++)

{

cur = nodeset->nodeTab[i];

cur = cur->xmlChildrenNode;

while(cur!=NULL)

{

value = xmlGetProp(cur,(const xmlChar *)"key");

if (value != NULL)

{

printf("value: %s\n\n", d_ConvertCharset("utf-8", "GBK", (char *)value));

xmlFree(value);

}

value = xmlNodeGetContent(cur);

if (value != NULL)

{

printf("value: %s\n\n", d_ConvertCharset("utf-8", "GBK", (char *)value));

xmlFree(value);

}

}

}

xmlXPathFreeObject (app_result);

}

通过get_nodeset()返回的结果集,我们可以获取该节点的元素及属性,也可以修改该节点的值。示例中在获取值打印的时候用到 d_ConvertCharset()函数来改变编码格式为GBK,以方便正确读取可能的中文字符。

5、编码问题

由于Libxml一般以UTF-8格式保存和操纵数据,如果你的程序使用其它的数据格式,比如中文字符(GB2312,GBK编码),就必须使用Libxml函数转换到UTF-8。如果你想你的程序以除UTF-8外的其它编码方式输出也必须做转换。

下面的示例程序提供几个函数来实现对数据编码格式的转换,其中有的要用到Libiconv,因此为了确保他们能正常工作,先检查以下系统中是否已经安装libiconv库。

示例6:

xmlChar *ConvertInput(const char *in, const char *encoding)

{

unsigned char *out;

int ret;

int size;

int out_size;

int temp;

xmlCharEncodingHandlerPtr handler;

if (in == 0)

return 0;

handler = xmlFindCharEncodingHandler(encoding);

if (!handler)

{

printf("ConvertInput: no encoding handler found for '%s'\n", encoding ? encoding : "");

return 0;

}

size = (int) strlen(in) + 1;

out_size = size * 2 - 1;

out = (unsigned char *) xmlMalloc((size_t) out_size);

if (out != 0) {

temp = size - 1;

ret = handler->input(out, &out_size, (const unsigned char *) in, &temp);

if ((ret < 0) || (temp - size + 1))

{

if (ret < 0)

{

printf("ConvertInput: conversion wasn't successful.\n");

}

else

{

printf("ConvertInput:conversion wasn't successful. converted: %i octets.\n", temp);

}

xmlFree(out);

out = 0;

}

else

{

out = (unsigned char *) xmlRealloc(out, out_size + 1);

out[out_size] = 0; /*null terminating out */

}

}

else

{

printf("ConvertInput: no mem\n");

}

return out;

}

示例7:

char * Convert( char *encFrom, char *encTo, const char * in)

{

static char bufin[1024], bufout[1024], *sin, *sout;

int mode, lenin, lenout, ret, nline;

iconv_t c_pt;

if ((c_pt = iconv_open(encTo, encFrom)) == (iconv_t)-1)

{

printf("iconv_open false: %s ==> %s\n", encFrom, encTo);

return NULL;

}

iconv(c_pt, NULL, NULL, NULL, NULL);

lenin = strlen(in) + 1;

lenout = 1024;

sin    = (char *)in;

sout   = bufout;

ret = iconv(c_pt, &sin, (size_t *)&lenin, &sout, (size_t *)&lenout);

if (ret == -1)

{

return NULL;

}

iconv_close(c_pt);

return bufout;

}

示例8:

char *d_ConvertCharset(char *cpEncodeFrom, char *cpEncodeTo, const char *cpInput)

{

static char s_strBufOut[1024], *sin, *cpOut;

size_t iInputLen, iOutLen, iReturn;

iconv_t c_pt;

if ((c_pt = iconv_open(cpEncodeTo, cpEncodeFrom)) == (iconv_t)-1)

{

printf("iconv_open failed!\n");

return NULL;

}

iconv(c_pt, NULL, NULL, NULL, NULL);

iInputLen = strlen(cpInput) + 1;

iOutLen = 1024;

sin   = (char *)cpInput;

cpOut = s_strBufOut;

iReturn = iconv(c_pt, &sin, &iInputLen, &cpOut, &iOutLen);

if (iReturn == -1)

{

return NULL;

}

iconv_close(c_pt);

return s_strBufOut;

}

通过上述函数,可以方便的在XML文件中保存并操纵中文字符。

Libxml2函数及使用方法概述【转】的更多相关文章

  1. paip.函数式编程方法概述以及总结

    paip.函数式编程方法概述以及总结 1     函数式编程:函数式风格..很多命令式语言里支持函数式编程风格 1.1      起源 (图灵机,Lisp机器, 神经网络计算机) 1.2      函 ...

  2. DBA_Oracle性能优化的基本方法概述(方法论)

    2014-12-18 Created By BaoXinjian

  3. (MTT)连续能量函数最小化方法

    (MTT)连续能量函数最小化方法 Multitarget tracking Multi-object tracking 连续能量函数 读"A.Milan,S. Roth, K. Schind ...

  4. jquery ajax success 函数 异步调用方法中不能给全局变量赋值的原因及解决办法

    jquery ajax success 函数 异步调用方法中不能给全局变量赋值的原因及解决办法   在调用一个jquery的ajax方法时我们有时会需要该方法返回一个值或者给某个全局变量赋值,可是我们 ...

  5. PHP使用内置函数生成图片的方法详解

    原文地址:http://www.poluoluo.com/jzxy/201605/475301.html 本文实例讲述了PHP使用内置函数生成图片的方法.分享给大家供大家参考,具体如下: 第一步:创建 ...

  6. JAVA 主函数(主方法)

    主函数(主方法) 1.public     (访问修饰符,公共的)代表该类或者该方法访问权限是最大的 2.static    代表主函数随着类的加载而加载 3.void    代表主函数没有具体的返回 ...

  7. 【转】Linux时间函数之gettimeofday()函数之使用方法

    原文网址:http://blog.csdn.net/tigerjibo/article/details/7039434 一.gettimeofday()函数的使用方法: 1.简介: 在C语言中可以使用 ...

  8. objective-C学习笔记(四)函数成员:方法(函数)

    函数分为:  全局函数(C语言函数) 成员函数(OBJC方法):实例方法 “-” 和类方法“+”的区别 //这里要区别静态变量(类变量).全局函数.常量的区别 OBJC里面,所有方法默认为公有方法.没 ...

  9. splice()函数的使用方法

    splice()函数的使用方法,这是一个拗口的函数.用起来有点麻烦.图3所看到的是splice函数的功能.将一个列表插入到还有一个列表其中.list容器类定义了splice()函数的3个版本号: sp ...

随机推荐

  1. Eclipse中从svn中检出maven项目

    相信很多初学者都遇到过Eclipse中从SVN检出Maven项目之后看到的目录结构并不是Maven目录结构:或者只能先用SVN将Maven项目签入到本地,然后再用Eclipse导入Maven项目,但是 ...

  2. 漏洞利用查询工具sandi

    漏洞利用查询工具sandi   在渗透测试中,一旦发现漏洞,就需要查找该漏洞的利用方式.由于漏洞众多,就需要渗透测试人员从海量的漏洞信息找出可用攻击载荷.Kali Linux内置了一个查询工具sand ...

  3. ios数据保存

  4. DIV+javascript实现首尾相连循环滚动效果

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  5. 使用 IntraWeb (7) - 主模板

    TIWLayoutMgrHTML.TIWTemplateProcessorHTML 属于页面级的模板, 如果要全站统一模板, 当然要用主模板. TIWTemplateProcessorHTML 通过其 ...

  6. Optimizing Oracle RAC

    Oracle Real Application Clusters (RAC) databases form an increasing proportion of Oracle database sy ...

  7. 百度公共dns

    常用公共DNS服务器地址 DNS(Domain Name System),即域名解析系统,作为将域名和IP地址相互映射的一个分布式数据库,能够使人通过相对好记的域名访问网站,而是一串数字.目前国内运营 ...

  8. delphi 实现Ribbon风格的窗体

    随着office2007的兴起,微软让我们看到了Ribbon风格的窗体,现在很多软件也都开始使用Ribbon风格.那么我们如果要自己开发,应当怎么做呢?本文就是为大家解开这个疑团的. 首先,Delph ...

  9. delphiredisclient开源GIT

    delphiredisclient - Redis client for Delphi Delphi Redis Client版本2(此分支)与Delphi 10.1 Berlin兼容,更好.警告!如 ...

  10. 多线程UI

    遇到过要在工作线程中去更新UI以让用户知道进度,而在多线程中直接调用UI控件操作是错误的做法. 最后解决方法是将操作UI的代码封装,通过Invoke / BeginInvoke 去委托调用. priv ...