libxml2对XML文件的创建、解析、查找、修改

2024-04-21 07:58

本文主要是介绍libxml2对XML文件的创建、解析、查找、修改,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、Libxml2介绍:

Libxml2 是一个xml的c语言版的解析器,本来是为Gnome项目开发的工具,是一个基于MIT License的免费开源软件。它除了支持c语言版以外,还支持c++、PHP、Pascal、Ruby、Tcl等语言的绑定,能在Windows、Linux、Solaris、MacOsX等平台上运行。功能还是相当强大的,相信满足一般用户需求没有任何问题。

二、 Libxml2安装:

一般如果在安装系统的时候选中了所有开发库和开发工具的话(Fedora Core系列下),应该不用安装,下面介绍一下手动安装:

1) 从xmlsoft站点或ftp(ftp.xmlsoft.org)站点下载libxml压缩包(libxml2-xxxx.tar.gz)

                2)      对压缩包进行解压缩

                  tar xvzf libxml2-xxxx.tar.gz

                3)      进入解压缩后的文件夹中运行

                                 ./configure

                                 make

                                 make install

安装完成后就可以使用简单的代码解析XML文件,包括本地和远程的文件,但是在编码上有一些问题。Libxml默认只支持UTF-8的编码,无论输入输出都是UTF-8,所以如果你解析完一个XML得到的结果都是UTF-8的,如果需要输出GB2312或者其它编码,需要ICONV来做转码(生成UTF-8编码的文件也可以用它做),如果系统中没有安装iconv的话,需要安装libiconv。

1) 下载libiconv压缩包(例如libiconv-1.11.tar.gz)

2) 对压缩包进行解压缩

    tar xvzf libiconv-1.11.tar.gz

3) 进入解压缩后的文件夹中运行

    ./configure

    make

    make install

三、关于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。

XML 解析器可以利用这种父子结构来遍历文档,甚至修改文档的结构或内容。LibXML2 是这样的解析器中的其中一种,并且文中的示例应用程序正是使用这种结构来实现该目的。对于各种不同的环境,有许多不同的解析器和库。LibXML2 是用于 UNIX 环境的解析器和库中最好的一种,并且经过扩展,它提供了对几种脚本语言的支持,如 Perl 和 Python。

四、使用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;       /* document pointer */

          xmlNodePtr root_node = NULL, node = NULL, node1 = NULL;/* node pointers */

   // Creates a new document, a node and set it as a root node

          doc = xmlNewDoc(BAD_CAST "1.0");

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

          xmlDocSetRootElement(doc, root_node);

          //creates a new node, which is "attached" as child node of root_node node.

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

          // xmlNewProp() creates attributes, which is "attached" to an node.

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

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

          //Here goes another way to create nodes.

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

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

          xmlAddChild(node, node1);

          xmlAddChild(root_node, node);

          //Dumping document to stdio or file

          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对XML文件的创建、解析、查找、修改的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/922554

相关文章

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

在cscode中通过maven创建java项目

在cscode中创建java项目 可以通过博客完成maven的导入 建立maven项目 使用快捷键 Ctrl + Shift + P 建立一个 Maven 项目 1 Ctrl + Shift + P 打开输入框2 输入 "> java create"3 选择 maven4 选择 No Archetype5 输入 域名6 输入项目名称7 建立一个文件目录存放项目,文件名一般为项目名8 确定

Java 创建图形用户界面(GUI)入门指南(Swing库 JFrame 类)概述

概述 基本概念 Java Swing 的架构 Java Swing 是一个为 Java 设计的 GUI 工具包,是 JAVA 基础类的一部分,基于 Java AWT 构建,提供了一系列轻量级、可定制的图形用户界面(GUI)组件。 与 AWT 相比,Swing 提供了许多比 AWT 更好的屏幕显示元素,更加灵活和可定制,具有更好的跨平台性能。 组件和容器 Java Swing 提供了许多

顺序表之创建,判满,插入,输出

文章目录 🍊自我介绍🍊创建一个空的顺序表,为结构体在堆区分配空间🍊插入数据🍊输出数据🍊判断顺序表是否满了,满了返回值1,否则返回0🍊main函数 你的点赞评论就是对博主最大的鼓励 当然喜欢的小伙伴可以:点赞+关注+评论+收藏(一键四连)哦~ 🍊自我介绍   Hello,大家好,我是小珑也要变强(也是小珑),我是易编程·终身成长社群的一名“创始团队·嘉宾”

OWASP十大安全漏洞解析

OWASP(开放式Web应用程序安全项目)发布的“十大安全漏洞”列表是Web应用程序安全领域的权威指南,它总结了Web应用程序中最常见、最危险的安全隐患。以下是对OWASP十大安全漏洞的详细解析: 1. 注入漏洞(Injection) 描述:攻击者通过在应用程序的输入数据中插入恶意代码,从而控制应用程序的行为。常见的注入类型包括SQL注入、OS命令注入、LDAP注入等。 影响:可能导致数据泄

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动

Maven创建项目中的groupId, artifactId, 和 version的意思

文章目录 groupIdartifactIdversionname groupId 定义:groupId 是 Maven 项目坐标的第一个部分,它通常表示项目的组织或公司的域名反转写法。例如,如果你为公司 example.com 开发软件,groupId 可能是 com.example。作用:groupId 被用来组织和分组相关的 Maven artifacts,这样可以避免

Spring 源码解读:自定义实现Bean定义的注册与解析

引言 在Spring框架中,Bean的注册与解析是整个依赖注入流程的核心步骤。通过Bean定义,Spring容器知道如何创建、配置和管理每个Bean实例。本篇文章将通过实现一个简化版的Bean定义注册与解析机制,帮助你理解Spring框架背后的设计逻辑。我们还将对比Spring中的BeanDefinition和BeanDefinitionRegistry,以全面掌握Bean注册和解析的核心原理。