zoukankan      html  css  js  c++  java
  • XML的创建、解析-C语言

      前言:今天在做一个小项目时,客户要求的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个字节
    ", 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
    ", 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
    ", 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整个流程都很长,不适合放在函数中!公司去华为的一个大神,先用双向链表创建再返回,封装一个函数,这样就很方便了,我也在继续优化中...

      

  • 相关阅读:
    python基础学习笔记第二天 内建方法(s t r)
    p ython笔记第三天
    (转)字符编码笔记:ASCII,Unicode和UTF8
    const char * 和 char *const
    自己的毕业设计windows管理软件
    自己实现string类
    关于vs2003环境支持win7 的 IP设置
    很犀利的错误
    很容易犯的错误
    用C++ 设计一个不能被继承的类
  • 原文地址:https://www.cnblogs.com/liudw-0215/p/9455235.html
Copyright © 2011-2022 走看看