XML的创建、解析-C语言

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介:   前言:今天在做一个小项目时,客户要求的xml,跟现在有系统要求的不一样,所以要自己重新写函数支持返回,进行简单总结,希望对大家有所帮助。  首先,使用xml函数需要链上动态库libxml2,需要在电脑上安装libxml的开发包,安装方法如下:      Ubuntu系统: sudo apt-...

  前言:今天在做一个小项目时,客户要求的xml,跟现在有系统要求的不一样,所以要自己重新写函数支持返回,进行简单总结,希望对大家有所帮助。

  首先,使用xml函数需要链上动态库libxml2,需要在电脑上安装libxml的开发包,安装方法如下:

      Ubuntu系统: sudo apt-get install libxml2-dev
      CentOS系统:yum install libxml2-devel  

1.   创建XML文档

  (1)相关函数有许多,网上也有特别多的解释,大家可以百度一下,这里只是简单介绍一部分;创建一个XML文档非常简单,其流程如下:

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

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

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

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

        ⑤    用xmlSaveFile将XML文档存入文件(用xmlDocDumpFormatMemoryEnc将XML存入内存)。

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

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

  注:xmlSaveFile存入文件方便单独执行程序查看结果,一般项目用用xmlDocDumpFormatMemoryEnc将XML存入内存!

   (2)创建xml文件举例 

#include <stdio.h>
#include <libxml/parser.h>
#include <libxml/tree.h>

int main()
{    
    xmlChar *result = NULL;
    int size = 0;
    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"));
    xmlNodePtr congson = xmlNewNode(NULL, BAD_CAST "congson");
    xmlAddChild(grandson,congson);
    
    //存储xml文档
    //xmlKeepBlanksDefault(0);
    //xmlDocDumpFormatMemoryEnc(doc, &result, &size, "UTF-8", 1);

    int nRel = xmlSaveFile("CreateXml.xml",doc);
    if (nRel != -1)
    {
        printf("一个xml文档被创建,写入%d个字节\n", nRel);
    }
    //释放文档内节点动态申请的内存
    xmlFreeDoc(doc);
    return 1;
}

  CentOS系统下面执行:gcc CreateXmlFile.c -o CreateXmlFile -I /usr/include/libxml2 -lxml2

    执行./CreateXmlFile,会生成一个XML文件CreatedXml.xml。

 2.   解析XML文档

 (1)XML解析流程

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

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

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

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

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

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

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

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

    (2). 解析XML文件并获取属性示例 

#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/include/libxml2  -lxml2

   执行:./ParseXmlFile  xxx.xml 

3、用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)来释放内存。

    Linux系统下有个iconv命令,可以在shell命令行输入iconv  --help 来查看用法,在程序中可以采用system系统调用来进行文件转码。下文中f表示from,t表示to,其转码方法如下:

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

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

 

    shell命令行使用方法: iconv -f gb2312 -t utf-8 readme.txt -o readme.txt

  总结:会发现创建xml整个流程都很长,不适合放在函数中!公司去华为的一个大神,先用双向链表创建再返回,封装一个函数,这样就很方便了,我也在继续优化中...

  

作者: 柳德维

-------------------------------------------

个性签名:独学而无友,则孤陋而寡闻。做一个灵魂有趣的人!

如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,博主在此感谢!

万水千山总是情,打赏一分行不行,所以如果你心情还比较高兴,也是可以扫码打赏博主,哈哈哈(っ•̀ω•́)っ⁾⁾!

目录
相关文章
|
29天前
|
存储 C语言 C++
【c语言】运算符汇总(万字解析)
今天博主跟大家分享了c语言中各种操作符的功能、使用方法以及优先级和结合性,并且与大家深入探讨了表达式求值的两个重要规则--算数转换和整形提升。学习这些知识对我们的C语言和C++学习都有着极大的帮助。
110 2
|
21天前
|
存储 网络协议 编译器
【C语言】深入解析C语言结构体:定义、声明与高级应用实践
通过根据需求合理选择结构体定义和声明的放置位置,并灵活结合动态内存分配、内存优化和数据结构设计,可以显著提高代码的可维护性和运行效率。在实际开发中,建议遵循以下原则: - **模块化设计**:尽可能封装实现细节,减少模块间的耦合。 - **内存管理**:明确动态分配与释放的责任,防止资源泄漏。 - **优化顺序**:合理排列结构体成员以减少内存占用。
105 14
|
25天前
|
存储 编译器 C语言
【C语言】数据类型全解析:编程效率提升的秘诀
在C语言中,合理选择和使用数据类型是编程的关键。通过深入理解基本数据类型和派生数据类型,掌握类型限定符和扩展技巧,可以编写出高效、稳定、可维护的代码。无论是在普通应用还是嵌入式系统中,数据类型的合理使用都能显著提升程序的性能和可靠性。
41 8
|
25天前
|
存储 算法 C语言
【C语言】深入浅出:C语言链表的全面解析
链表是一种重要的基础数据结构,适用于频繁的插入和删除操作。通过本篇详细讲解了单链表、双向链表和循环链表的概念和实现,以及各类常用操作的示例代码。掌握链表的使用对于理解更复杂的数据结构和算法具有重要意义。
268 6
|
25天前
|
存储 网络协议 算法
【C语言】进制转换无难事:二进制、十进制、八进制与十六进制的全解析与实例
进制转换是计算机编程中常见的操作。在C语言中,了解如何在不同进制之间转换数据对于处理和显示数据非常重要。本文将详细介绍如何在二进制、十进制、八进制和十六进制之间进行转换。
34 5
|
25天前
|
C语言 开发者
【C语言】断言函数 -《深入解析C语言调试利器 !》
断言(assert)是一种调试工具,用于在程序运行时检查某些条件是否成立。如果条件不成立,断言会触发错误,并通常会终止程序的执行。断言有助于在开发和测试阶段捕捉逻辑错误。
35 5
|
25天前
|
安全 搜索推荐 Unix
【C语言】《回调函数》详细解析
回调函数是指一个通过函数指针调用的函数。它允许将一个函数作为参数传递给另一个函数,并在特定事件发生时执行。这种技术使得编程更加灵活,可以动态决定在何时调用哪个函数。
40 1
|
1月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
77 2
|
3天前
|
存储 设计模式 算法
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。 行为型模式分为: • 模板方法模式 • 策略模式 • 命令模式 • 职责链模式 • 状态模式 • 观察者模式 • 中介者模式 • 迭代器模式 • 访问者模式 • 备忘录模式 • 解释器模式
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
|
3天前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。 结构型模式分为以下 7 种: • 代理模式 • 适配器模式 • 装饰者模式 • 桥接模式 • 外观模式 • 组合模式 • 享元模式
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析

推荐镜像

更多