XML常见的操作

1.   创建XML文档

(1)创建一个XML文档非常简单,其流程如下:

①    用xmlNewDoc函数创建一个文档指针doc。

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

③    用xmlDocSetRootElement将root_node设置为doc的根结点。

④    给root_node添加一系列的子节点,并设置子节点的内容和属性。

⑤    用xmlSaveFile将XML文档存入文件。

⑥    用xmlFreeDoc关闭文档指针,并清除本文档中所有节点动态申请的内存。

有多种方式可以添加子节点,如可以用xmlNewTextChild直接添加一个文本子节点。也可以先创建新节点,然后用xmlAddChild将新节点加入到上层节点中。

 

(2)创建xml文件举例

CreateXmlFile.c源代码如下:

#include <stdio.h>

#include <libxml/parser.h>

#include <libxml/tree.h>

int main()

{

  //定义文档和节点指针

  xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");

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

     //设置根节点

     xmlDocSetRootElement(doc,root_node);

     //在根节点中直接创建节点

     xmlNewTextChild(root_node, NULL, BAD_CAST "newNode1", BAD_CAST "newNode1 content");

     xmlNewTextChild(root_node, NULL, BAD_CAST "newNode2", BAD_CAST "newNode2 content");

     xmlNewTextChild(root_node, NULL, BAD_CAST "newNode3", BAD_CAST "newNode3 content");

     //创建一个节点,设置其内容和属性,然后加入根结点

     xmlNodePtr node = xmlNewNode(NULL,BAD_CAST "node2");

     xmlNodePtr content = xmlNewText(BAD_CAST "NODE CONTENT");

     xmlAddChild(root_node,node);

     xmlAddChild(node,content);

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

     //创建一个儿子和孙子节点

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

     xmlAddChild(root_node,node);

     xmlNodePtr grandson = xmlNewNode(NULL, BAD_CAST "grandson");

     xmlAddChild(node,grandson);

     xmlAddChild(grandson, xmlNewText(BAD_CAST "This is a grandson node"));

     //存储xml文档

     int nRel = xmlSaveFile("CreateXml.xml",doc);

     if (nRel != -1)

     {

        printf("一个xml文档被创建,写入%d个字节\n", nRel);

     }

     //释放文档内节点动态申请的内存

     xmlFreeDoc(doc);

     return 1;

}

编译 gcc CreateXmlFile.c -o CreateXmlFile -I/usr/local/include/libxml2       -lxml2。

执行./CreateXmlFile,会生成一个XML文件CreatedXml.xml。打开后如下所示:

<?xml version="1.0"?>

<root>

    <newNode1>newNode1 content</newNode1>

    <newNode2>newNode2 content</newNode2>

    <newNode3>newNode3 content</newNode3>

    <node2 attribute="yes">NODE CONTENT</node2>

    <son>

       <grandson>This is a grandson node</grandson>

    </son>

</root>

最好使用类似XMLSPY这样的工具打开,因为这些工具可以自动整理XML文件的栅格,否则很有可能是没有任何换行的一个XML文件,可读性较差。

2.   解析XML文档

(1)XML解析流程

解析一个XML文档,从中取出想要的信息,例如节点中包含的文字,或者某个节点的属性。其流程如下:

①    用xmlReadFile函数读入一个文件,并返回一个文档指针doc。

②    用xmlDocGetRootElement函数得到根节点curNode。

③    此时curNode->xmlChildrenNode就是根节点的首个儿子节点,该儿子节点的兄弟节点可用next指针进行轮询。

④    轮询所有子节点,找到所需的节点,用xmlNodeGetContent取出其内容。

⑤    用xmlHasProp查找含有某个属性的节点,属性列表指针xmlAttrPtr将指向该节点的属性列表。

⑥    取出该节点的属性,用xmlGetProp取出其属性值。

⑦    xmlFreeDoc函数关闭文档指针,并清除本文档中所有节点动态申请的内存。

 

(2)XML解析举例

ParseXmlFile.c源代码如下:

#include <stdio.h>

#include <libxml/parser.h>

#include <libxml/tree.h>

int main(int argc, char* argv[])

{

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

    xmlNodePtr curNode;      //定义结点指针

    xmlChar *szKey;          //临时字符串变量

    char *szDocName;

    if (argc <= 1)

    {

        printf("Usage: %s docname", argv[0]);

        return(0);

    }

    szDocName = argv[1];

    doc = xmlReadFile(szDocName,"GB2312",XML_PARSE_RECOVER);

    //解析文件

    //检查解析文档是否成功,如果不成功,libxml将报错并停止解析。

    //一个常见错误是不适当的编码,XML标准文档除了用UTF-8或UTF-16外还可用其它编码保存

    if (NULL == doc)

    { 

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

        return -1;

    }

    //获取根节点

    curNode = xmlDocGetRootElement(doc);

    if (NULL == curNode)

    {

        fprintf(stderr,"empty document");

        xmlFreeDoc(doc);

        return -1;

    }

    //确认根元素名字是否符合

    if (xmlStrcmp(curNode->name, BAD_CAST "root"))

    {

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

        xmlFreeDoc(doc);

        return -1;

    }

    curNode = curNode->xmlChildrenNode;

    xmlNodePtr propNodePtr = curNode;

    while(curNode != NULL)

    {

        //取出节点中的内容

        if ((!xmlStrcmp(curNode->name, (const xmlChar *) "newNode1")))

        {

            szKey = xmlNodeGetContent(curNode);

            printf("newNode1: %s\n", szKey);

            xmlFree(szKey);

        }

        //查找带有属性attribute的节点

        if (xmlHasProp(curNode,BAD_CAST "attribute"))

        {

            propNodePtr = curNode;

        }

        curNode = curNode->next;

    }

    //查找属性

    xmlAttrPtr attrPtr = propNodePtr->properties;

    while (attrPtr != NULL)

    {

        if (!xmlStrcmp(attrPtr->name, BAD_CAST "attribute"))

        {

            xmlChar* szAttr = xmlGetProp(propNodePtr,BAD_CAST "attribute");

            printf("get attribute=%s\n", szAttr) ;

            xmlFree(szAttr);

        }

        attrPtr = attrPtr->next;

    }

    xmlFreeDoc(doc);

    return 0;

}

编译 gcc ParseXmlFile.c -o ParseXmlFile -I/usr/local/include/libxml2  -lxml2。

执行 ./ParseXmlFile CreateXml.xml,执行结果如下:

newNode1: newNode1 content

get attribute=yes

3.   修改XML文档

有了上面的基础,修改XML文档的内容就简单了。首先打开一个已经存在的XML文档,顺着根结点找到需要添加、删除、修改的地方,调用相应的XML函数对节点进行增、删、改操作。

需要注意的是,并没有xmlDelNode或者xmlRemoveNode函数,删除节点需使用以下一段代码:

       if (!xmlStrcmp(curNode->name, BAD_CAST "newNode1"))

       {

           xmlNodePtr tempNode;

           tempNode = curNode->next;

           xmlUnlinkNode(curNode);

           xmlFreeNode(curNode);

           curNode = tempNode;

           continue;

       }

此段代码完成将当前节点从文档中断链(unlink),这样此XML文档就不会再包含这个节点,该节点断链后需使用xmlFreeNode来释放该节点申请的动态内存空间。

4.   使用XPath查找XML文档

在libxml2中使用XPath非常简单,其流程如下:

①    定义一个XPath上下文指针xmlXPathContextPtr context,并且使用xmlXPathNewContext函数来初始化这个指针。

②    定义一个XPath对象指针xmlXPathObjectPtr result,并且使用xmlXPathEvalExpression函数来计算XPath表达式,得到查询结果,将结果存入对象指针中。

③    使用result->nodesetval得到节点集合指针,其中包含了所有符合XPath查询结果的节点。

④    使用xmlXPathFreeContext释放上下文指针。

⑤    使用xmlXPathFreeObject释放XPath对象指针。

 

XPath操作代码示例如下:

xmlXPathObjectPtr getNodeSet(xmlDocPtr doc, const xmlChar *szXpath)

{

    xmlXPathContextPtr context;    //XPath上下文指针

    xmlXPathObjectPtr result;       //XPath对象指针,用来存储查询结果

    context = xmlXPathNewContext(doc);     //创建一个XPath上下文指针

    if (context == NULL)

    {  

       printf("context is NULL"n");

       return NULL;

    }

    result = xmlXPathEvalExpression(szXpath, context); //查询XPath表达式,得到一个查询结果

    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;   

}

5.   用iconv解决XML中字符集问题

libxml2中默认的内码是UTF-8,所有使用libxml2进行处理的xml文件,必须首先显式或者默认转换为UTF-8编码才能被处理。

要在XML中使用中文,就必须能够在UTF-8和GB2312之间进行转换。libxml2提供了默认的内码转换机制,并且在libxml2的Tutorial中有一个例子,事实证明这个例子并不很适合用来转换中文。

有些场合需要使用iconv来进行编码转换,libxml2本身也是使用iconv进行编码转换的。iconv是一个专门用来进行编码转换的库,基本上支持目前所有常用的编码,它是glibc库的一个部分。

本节其实和libxml没有太大关系,可以把它简单看作是一个编码转换方面的专题。下文提供了一个通用转码函数,并在此基础上实现了两个转码封装函数,即从UTF-8转换到GB2312的函数u2g,以及反向转换的函数g2u。其代码如下:

#include <iconv.h>

#include <string.h>

//代码转换,从一种编码转为另一种编码  

int code_convert(char* from_charset, char* to_charset, char* inbuf,

               int inlen, char* outbuf, int outlen)

{

    iconv_t cd;

    char** pin = &inbuf;   

    char** pout = &outbuf;

    cd = iconv_open(to_charset,from_charset);  

    if(cd == 0)

       return -1;

    memset(outbuf,0,outlen);  

    if(iconv(cd,(const char**)pin,(unsigned int *)&inlen,pout,(unsigned int*)&outlen)

       == -1)

       return -1;  

    iconv_close(cd);

    return 0;  

}

//UNICODE码转为GB2312码  

//成功则返回一个动态分配的char*变量,需要在使用完毕后手动free,失败返回NULL

char* u2g(char *inbuf)  

{

    int nOutLen = 2 * strlen(inbuf) - 1;

    char* szOut = (char*)malloc(nOutLen);

    if (-1 == code_convert("utf-8","gb2312",inbuf,strlen(inbuf),szOut,nOutLen))

    {

       free(szOut);

       szOut = NULL;

    }

    return szOut;

}  

//GB2312码转为UNICODE码  

//成功则返回一个动态分配的char*变量,需要在使用完毕后手动free,失败返回NULL

char* g2u(char *inbuf)  

{

    int nOutLen = 2 * strlen(inbuf) - 1;

    char* szOut = (char*)malloc(nOutLen);

    if (-1 == code_convert("gb2312","utf-8",inbuf,strlen(inbuf),szOut,nOutLen))

    {

       free(szOut);

       szOut = NULL;

    }

    return szOut;

}

下面以UTF-8到GB2312转码流程说明上文中转码函数的使用,使用流程如下:

①    得到一个UTF-8的字符串szSrc。

②    定义一个char *的字符指针szDes,并不需要给它动态申请内存。

③    调用szDes = u2g(szSrc),这样szDes就指向转换后GB2312编码的字符串。

④    使用完这个字符串后使用free(szDes)来释放内存。

如果转码文件可以选择系统调用来进行文件转码。下文中f表示from,t表示to,其转码方法如下:

system("iconv –f 源格式 –t 目标格式 源文件 >目标文件")

system("iconv –f  GB18030 –t UTF-8  test_gb.txt > test_utf.txt")

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/445080.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

算法(2)-二叉树的遍历(递归/迭代)python实现

二叉树的遍历1.深度优先DFS1.1 DFS 递归解法1.1.1先序遍历1.1.2中序遍历1.1.3后序遍历1.2 DFS迭代解法1.2.1先序遍历1.2.2中序遍历1.2.3后序遍历2.广度优先BFS3.二叉树的最大深度3.1递归3.2迭代4.翻转二叉树4.1递归4.1迭代5.合并两棵二叉树5.1递归5.2迭代有两种通用的遍历树的策…

libxml的安装和相关数据结构详解

1安装 一般如果在安装系统的时候选中了libxml开发库的话&#xff0c;系统会默认安装。如果没有安装&#xff0c;可以按如下步骤进行手工安装。 ① 从xmlsoft站点或ftp(ftp.xmlsoft.org)站点下载libxml压缩包 (libxml2-xxxx.tar.gz) ② 对压缩包进行解压缩 tar xvzf …

内核中的 likely() 与 unlikely()

在 2.6 内核中&#xff0c;随处可以见到 likely() 和 unlikely() 的身影&#xff0c;那么为什么要用它们&#xff1f;它们之间有什么区别&#xff1f; 首先要明确&#xff1a; if(likely(value)) 等价于 if(value) if(unlikely(value)) 也等价于 if(value) 也就是说 likely()…

python外卷(12)-sort(),sorted(),ord(),chr()

Python内置函数1.sort()&#xff0c;sorted()2.ord(), chr()1.sort()&#xff0c;sorted() sort() 是list的方法&#xff0c;对已经存在的列表进行操作&#xff0c;无返回值 a[3,2,4,1] b["c","a","b"] print (a.sort(),b.sort()) # 输出 (Non…

利用posix_fadvise清理系统中的文件缓存

利用posix_fadvise清理系统中的文件缓存leoncom c/c,unix2011-08-03当我们需要对某段读写文件并进行处理的程序进行性能测试时&#xff0c;文件会被系统cache住从而影响I/O的效率&#xff0c;必须清理cache中的对应文件的才能正确的进行性能测试。通常清理内存可以采用下面的这…

空间分配

目前主流的垃圾收集器都会采用分代回收算法&#xff0c;因此需要将堆内存分为新生代和老年代&#xff0c;这样我们就可以根据各个年代的特点选择合适的垃圾收集算法。 大多数情况下&#xff0c;对象在新生代中 eden 区分配。当 eden 区没有足够空间进行分配时&#xff0c;虚拟…

关于uint32_t uint8_t uint64_t 的问题

怎么又是u又是_t的?u代表的是unsigned相信大家都知道,那么_t又是什么呢?我认为它就是一个结构的标注,可以理解为type/typedef的缩写,表示它是通过typedef定义的,而不是其它数据类型。 uint8_t,uint16_t,uint32_t等都不是什么新的数据类型,它们只是使用typedef给类型起…

学点数学(4)-协方差矩阵

协方差矩阵协方差矩阵&#xff08;从随机变量讲起&#xff09;随机变量x&#xff1a;表示随机试验各种结果的 实值 单值函数&#xff0c;就是说随机变量x是一个函数映射&#xff0c;其取值为标量。随机变量有离散型和连续型&#xff0c;离散型&#xff1a;抛10次硬币&#xff…

RedLock

概念 Redis 官方站这篇文章提出了一种权威的基于 Redis 实现分布式锁的方式名叫 Redlock&#xff0c;此种方式比原先的单节点的方法更安全。它可以保证以下特性&#xff1a; 安全特性&#xff1a;互斥访问&#xff0c;即永远只有一个 client 能拿到锁避免死锁&#xff1a;最终…

GCC中常用的优化的参数

-pipe 的作用&#xff1a; 使用管道代替编译中临时文件&#xff0c; -pipe 加速编译 gcc -pipe foo.c -o foo 加速 在将源代码变成可执行文件的过程中,需要经过许多中间步骤,包含预处理、编译、汇编和连接。这些过程实际上是由不同的程序负责完成的。大多数情况下 GCC 可以为 …

Linux与时间相关的结构体及相关用法

1. Linux下与时间有关的结构体 struct timeval { int tv_sec; int tv_usec; }; 其中tv_sec是由凌晨开始算起的秒数&#xff0c;tv_usec则是微秒(10E-6 second)。 struct timezone { …

算法(3)-数据结构-数组和字符串

leetcode-explore-learn-数据结构-数据结构-数组和字符串1. 一维数组1.0 概况1.1 寻找数组的中心索引1.2 搜索插入位置1.3 合并区间1.4 至少是其他数字两倍大的最大数1.5 加一2. 二维数组2.1旋转矩阵本系列博文为leetcode-explore-learn子栏目学习笔记&#xff0c;如有不详之处…

redis的入门/原理/实战大总结

入门 Redis是一款基于键值对的NoSQL数据库&#xff0c;它的值支持多种数据结构&#xff1a; 字符串(strings)、哈希(hashes)、列表(lists)、集合(sets)、有序集合(sorted sets)等。 • Redis将所有的数据都存放在内存中&#xff0c;所以它的读写性能十分惊人&#xff0c;用作数…

创建与打开IPC通道的POSIX和SYSTEM V方法

先说&#xff30;&#xff2f;&#xff33;&#xff29;&#xff38;的吧&#xff1a; mq_open&#xff0c;sem_open&#xff0c;shm_open着三个函数用于创建或者打开一个IPC通道。 由此可见&#xff0c;消息队列的读写权限是任意的&#xff0c;然而信号灯就没有&#xff0c;…

算法(4)-leetcode-explore-learn-数据结构-数组2

leetcode-explore-learn-数据结构-数组21.简述2.例题2.1 二维数组的对角线遍历2.2 螺旋遍历2.3 杨辉三角本系列博文为leetcode-explore-learn子栏目学习笔记&#xff0c;如有不详之处&#xff0c;请参考leetcode官网&#xff1a;https://leetcode-cn.com/explore/learn/card/ar…

软件测试基础知识

第一章 1.1 软件测试背景知识和发展史 互联网公司职位架构&#xff1a;产品 运营 技术 市场 行政软件测试&#xff1a;使用人工或自动化手段&#xff0c;来运行或测试某个系统的过程&#xff0c;其目的在于检验它是否满足规定的需求或弄清预期结果与实际结果之间的差别&#…

key_t IPC键和ftok函数详解和剖析

统建立IPC通讯&#xff08;如消息队列、共享内存时&#xff09;必须指定一个ID值。通常情况下&#xff0c;该id值通过ftok函数得到。 ftok原型如下&#xff1a; key_t ftok( char * fname, int id ) fname就时你指定的文件名(该文件必须是存在而且可以访问的)&#xff0c;id是子…

算法(5)-leetcode-explore-learn-数据结构-字符串

leetcode-explore-learn-数据结构-数组3-字符串1.简述2.例题2.1 二进制求和2.2实现strStr()2.3最长公共前缀本系列博文为leetcode-explore-learn子栏目学习笔记&#xff0c;如有不详之处&#xff0c;请参考leetcode官网&#xff1a;https://leetcode-cn.com/explore/learn/card…

ipcs命令查看管道,消息队列,共享内存

修改消息队列大小&#xff1a; root&#xff1a;用户&#xff1a; /etc/sysctl.conf kernel.msgmnb 4203520 #kernel.msgmnb 3520 kernel.msgmni 2878 保存后需要执行 sysctl -p ,然后重建所有消息队列 ipcs -q : 显示所有的消息队列 ipcs -qt : 显示消息队列的创建时…

Jmeter-基础篇

常用压力测试工具对比 1、loadrunner 性能稳定&#xff0c;压测结果及细粒度大&#xff0c;可以自定义脚本进行压测&#xff0c;但是太过于重大&#xff0c;功能比较繁多 2、apache ab(单接口压测最方便) 模拟多线程并发请求,ab命令对发出负载的计算机…