参考文章:
libxml2的安装及使用_阿卡基YUAN的博客-CSDN博客
Xml文件介绍
解析XML文件
libxml2的安装
参考安装:Linux如何安装并配置libxml2库?解决“libxml2 not found“问题_Mintimate的博客-CSDN博客
指令安装
Linux是Debian或Ubuntu:
sudo apt-get install libxml2
sudo apt-get install libxml2-dev
编译安装
可参考libxml2的官方网址: Home · Wiki · GNOME / libxml2 · GitLab
下载最新的libxml2库: Releases · GNOME / libxml2 · GitLab
安装参考: c语言读取xml配置文件-CSDN博客
具体安装步骤:
-
解压:$tar zxvf libxml2-2.9.1.tar.gz
-
进入解压后的安装目录:$cd libxml2-2.9.1
-
配置libxml2库
- ./configure
- make
- make install
-
执行配置命令
./configure
- 编译过程中出现出错
libxml.c:14:20: fatal error: Python.h: No such file or directory
需要安装python,执行命令:
sudo apt-get install python-dev
安装完python-dev之后,再次编译成功。
- 执行make install执行安装
安装完成之后,查看output路径下,增加了相关的文件。
XML文件类型
xml配置文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<bmp_para>
<para id="1">
<width>1920</width>
<height>1080</height>
<bit>3</bit>
<blue>0</blue>
<green>0</green>
<red>255</red>
</para>
</bmp_para>
内部字符类型xmlChar
xmlChar是Libxml2中的字符类型,库中所有字符、字符串都是基于这个数据类型。事实上他的定义是:xmlstring.h
#incldue<xmlstring.h>
typedef unsigned char xmlChar;
使用unsigned char作为内部字符格式是考虑到他能非常好适应UTF-8编码,而UTF-8编码正是libxml2的内部编码,其他格式的编码要转换为这个编码才能在libxml2中使用。
xmlChar相关函数
如同标准c中的char类型相同,xmlChar也有动态内存分配、字符串操作等相关函数
- xmlMalloc是动态分配内存的函数;
- xmlFree是配套的释放内存函数;
- xmlStrcmp是字符串比较函数等等。
基本上xmlChar字符串相关函数都在xmlstring.h中定义;而动态内存分配函数在xmlmemory.h中定义
xmlChar*和其他类型之间的转换
另外要注意,因为总是要在xmlChar和char之间进行类型转换,所以定义了一个宏BAD_CAST,其定义如下:xmlstring.h
#include<xmlstring.h>
#define BAD_CAST (xmlChar *)
原则上来说,unsigned char和char之间进行强制类型转换是没有问题的。
文件类型xmlDoc、指针xmlDocPtr
xmlDoc是个struct,保存了一个xml的相关信息,例如文件名、文件类型、子节点等等;xmlDocPtr等于xmlDoc*,他搞成这个样子总让人以为是智能指针,其实不是,要手动删除的。
- **xmlNewDoc: **创建一个新的文件指针。
- **xmlParseFile: **以默认方式读入一个UTF-8格式的文件,并返回文件指针
- **xmlReadFile: **读入一个带有某种编码的xml文件,并返回文件指针;
- **xmlFreeDoc: **释放文件指针。特别注意,当你调用xmlFreeDoc时,该文件所有包含的节点内存都被释放,所以一般来说不必手动调用xmlFreeNode或xmlFreeNodeList来释放动态分配的节点内存,除非你把该节点从文件中移除了。一般来说,一个文件中所有节点都应该动态分配,然后加入文件,最后调用xmlFreeDoc一次释放所有节点申请的动态内存,这也是为什么我们非常少看见xmlNodeFree的原因。
- **xmlSaveFile: **将文件以默认方式存入一个文件。
- **xmlSaveFormatFileEnc: **可将文件以某种编码/格式存入一个文件中。
节点类型xmlNode、指针xmlNodePtr
节点应该是xml中最重要的元素了,xmlNode代表了xml文件中的一个节点,实现为一个struct,内容非常丰富:tree.h
#include<tree.h>
typedef struct _xmlNode xmlNode;
typedef xmlNode *xmlNodePtr;
struct _xmlNode {
void *_private;/* application data */
xmlElementType type; /* type number, must be second ! */
const xmlChar *name; /* the name of the node, or the entity */
struct _xmlNode *children; /* parent->childs link */
struct _xmlNode *last; /* last child link */
struct _xmlNode *parent;/* child->parent link */
struct _xmlNode *next; /* next sibling link */
struct _xmlNode *prev; /* previous sibling link */
struct _xmlDoc *doc;/* the containing document */
/* End of common part */
xmlNs *ns; /* pointer to the associated namespace */
xmlChar *content; /* the content */
struct _xmlAttr *properties;/* properties list */
xmlNs *nsDef; /* namespace definitions on this node */
void *psvi;/* for type/PSVI informations */
unsigned short line; /* line number */
unsigned short extra; /* extra data for XPath/XSLT */
};
能看到,节点之间是以链表和树两种方式同时组织起来的,next和prev指针能组成链表,而parent和children能组织为树。同时更有以下重要元素:
l 节点中的文字内容:content;
l 节点所属文件:doc;
l 节点名字:name;
l 节点的namespace:ns;
l 节点属性列表:properties;
Xml文件的操作其根本原理就是在节点之间移动、查询节点的各项信息,并进行增加、删除、修改的操作
xmlDocSetRootElement函数能将一个节点设置为某个文件的根节点,这是将文件和节点连接起来的重要手段,当有了根结点以后,所有子节点就能依次连接上根节点,从而组织成为一个xml树。
节点集合类型xmlNodeSet、指针xmlNodeSetPtr
节点集合代表一个由节点组成的变量,节点集合只作为Xpath的查询结果而出现(XPATH的介绍见后面),因此被定义在xpath.h中,其定义如下:
#include<xpath.h>
/*
* A node-set (an unordered collection of nodes without duplicates).
*/
typedef struct _xmlNodeSet xmlNodeSet;
typedef xmlNodeSet *xmlNodeSetPtr;
struct _xmlNodeSet {
int nodeNr; /* number of nodes in the set */
int nodeMax; /* size of the array as allocated */
xmlNodePtr *nodeTab; /* array of nodes in no particular order */
/* @@ with_ns to check wether namespace nodes should be looked at @@ */
};
节点集合有三个成员,分别是节点集合的节点数nodeNr、最大可容纳的节点数nodeMax,及节点数组头指针nodeTab。
对节点集合中各个节点的访问方式非常简单,如下:
xmlNodeSetPtr nodeset = XPATH查询结果;
for (int i = 0; i < nodeNr; i++)
{
nodeset->nodeTab;
}
注意:libxml2是个c函数库,因此其函数和数据类型都使用c语言的方式来处理。
xml文档结构
xml按照树形结构进行存储,节点分为元素和文本,必须有根节点。如下的xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<phone_books>
<phone id="1">
<name>Anker</name>
<tel>18923873456</tel>
<address>Shenzheng</address>
</phone>
<phone id="2">
<name>Jermey</name>
<tel>18623873456</tel>
<address>Beijing</address>
</phone>
<phone id="3">
<name>Lili</name>
<tel>13223873456</tel>
<address>Shanghai</address>
</phone>
</phone_books>
实操练习
libxml2常用的接口如下:
内部字符类型:xmlChar,用无符号型的char方便表示utf-8编码。libxml2提供了一个宏进行转换,#define BAD_CAST (xmlChar *)。
文档类型xmlDoc,指针类型xmlDocPtr。
xmlDoc是个struct,保存了一个xml的相关信息,例如文件名、文件类型、子节点等等;xmlDocPtr等于xmlDoc*。
xmlNewDoc函数创建一个新的文件指针。
xmlParseFile函数以默认方式读入一个UTF-8格式的文件,并返回文件指针。
xmlReadFile函数读入一个带有某种编码的xml文件,并返回文件指针
xmlFreeDoc释放文件指针。特别注意,当你调用xmlFreeDoc时,该文件所有包含的节点内存都被释放
xmlFreeNodeList来释放动态分配的节点内存,除非你把该节点从文件中移除了。
xmlSaveFile将文件以默认方式存入一个文件。
xmlSaveFormatFileEnc可将文件以某种编码/格式存入一个文件中。
节点类型xmlNode、指针xmlNodePtr
xmlDocSetRootElement函数能将一个节点设置为某个文件的根节点
创建xml文件
创建流程:
- 用xmlNewDoc函数创建一个文件指针doc;
- 用xmlNewNode函数创建一个节点指针root_node;
- 用xmlDocSetRootElement函数讲root_node设置为doc的根节点;
- 给root_node添加一系列的子节点, 并设置子节点的内容和属性;
- 用xmlSaveFile函数讲xml文件存入文件
- 用xmlFreeDoc函数关闭文件指针, 并清除本文件中所有节点动态申请的内存
可以有多种方式添加子节点:
- 用xmlNewTextChild函数直接添加一个文本子节点
- 先创建新节点, 然后用xmlAddChild将新节点加入上层节点
源码分析:
#include<stdio.h>
#include<stdlib.h>
#include<libxml/parser.h>
#include<libxml/tree.h>
#include<libxml/xmlmemory.h>
int main(int argc, char *argv[]){
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(root_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 grandson Node"));
//存储xml文件
int nRet = xmlSaveFile("CreatedXml.xml",doc);
if(nRet != -1){
printf("xml creat success\n");
xmlFreeDoc(doc);
}
return 0;
}
参考学习: “libxml/parser.h: 没有那个文件或目录”解决方案_iamlate的博客-CSDN博客
- make编译,你应该会报错, “找不到parser.h等头文件”
- 然后你去查找一下你会发现, 头文件竟然在src里找到了
- 进入/usr/include目录, 你会发现你要找的头文件都是在该路径下,**/usr/include/libxml2/libxml/…h. **
- 因为安装好的库,头文件默认是在libxml2的目录下, 所以我们需要做一个软链接.
sudo ln -s /usr/include/libxml2/libxml /usr/include/libxml
- 然后重新make编译, 你会发现又报错. 哈哈
这里是报错的原因是你一些文件没有链接成功, 加上libxml2.so的动态库就行了.
- 加上动态库,重新编译
gcc xml.c -o xml -I/usr/include/libxml -lxml2
- 运行成功,
- 此时会生成一个CreateXml.xml文件
<?xml version="1.0"?>
<root>
<newNode1>
newNode1 content
</newNode1>
<newNode2>
newNode2 content
</newNode2>
<newNode3>
newNode3 content
</newNode3>
<node2 attribute="yes"/>NODE CONTENT
<son>
<grandson>This is grandson Node</grandson>
</son>
</root>
附上Makefile文件, 供大家查看, 也正好锻炼一下Makefile
CC := gcc
SRC := $(wildcard *.c)
OBJS := $(patsubst %.c,%.o, ${SRC})
TARGET := $(basename ${OBJS})
INCLUDE_DIR := /usr/include/libxml
CFLAGS+= -I$(INCLUDE_DIR)/
LIBS+= -lxml2
add: $(TARGET)
$(TARGET): $(OBJS)
$(CC) $^ -o $@ $(CFLAGS) $(LIBS)
%.o: %.c
$(CC) -c $< -o $@
clean:
-rm -rf $(OBJS)
-rm -rf $(TARGET)
解析xml文档
在上面,我们学会了如何创建xml文件. 这节学习如何解析已存在的xml文件
解析一个xml文件,从中读取信息, 例如节点中的文字, 或某个节点的属性…
解析流程如下:
- 用xmlReadFile函数读出一个文件指针doc;
- 用xmlDocGetRootElement函数得到根节点curNode;
- curNode->xmlChildrenNode就是根节点的子节点集合;
- 轮询子节点集合, 找到所需要的节点, 用xmlNodeGetContent取出内容;
- 用xmlHasProp查找含有某个属性的节点;
- 取出该节点的属性集合, 用xmlGetProp取出其属性值;
- 用xmlFreeDoc函数关闭文件指针, 并清除本文中的所有节点申请的动态内存;
<?xml version="1.0"?>
<root>
<newNode1>
newNode1 content
</newNode1>
<newNode2>
newNode2 content
</newNode2>
<newNode3>
newNode3 content
</newNode3>
<node2 attribute="yes"/>NODE CONTENT
<son>
<grandson>This is grandson Node</grandson>
</son>
</root>
源码解析:文章来源:https://www.toymoban.com/news/detail-712614.html
#include<stdio.h>
#include<stdlib.h>
#include<libxml/parser.h>
#include<libxml/tree.h>
#include<libxml/xmlmemory.h>
#include<libxml/xmlstring.h>
int main(int argc, char* argv[]){
xmlDocPtr doc; //定义解析文件指针
xmlNodePtr curNode; //定义根节点指针
xmlChar *szKey; //临时字符串变量
char *szDocName;
if(argc < 2){
printf("Usage: %s <xml>\n", argv[0]);
exit(EXIT_FAILURE);
}
szDocName = argv[1]; //保存xml文件名
doc = xmlReadFile(szDocName, "GB2312", XML_PARSE_RECOVER); //解析文件
//检查是否解析成功
if(doc == NULL){
fprintf(stderr, "Document parse failure\n");
exit(EXIT_FAILURE);
}
curNode = xmlDocGetRootElement(doc); //确定根节点
//先检查当前xml文件里是否是空
if(curNode == NULL){
fprintf(stderr, "empty xml file\n");
exit(EXIT_FAILURE);
}
if(xmlStrcmp(curNode->name, "root") != 0){ //判断是否是根节点
fprintf(stderr, "Type error\n");
exit(EXIT_FAILURE);
}
curNode = curNode->xmlChildrenNode; //遍历子节点 next
xmlNodePtr propNodeptr = curNode;
while(curNode != NULL){
if((xmlStrcmp(curNode->name, (const xmlChar *)"newNode2")) == 0){ //取出子节点的名字
szKey = xmlNodeGetContent(curNode); //保存子节点的名字
printf("newNode1: %s\n", szKey);
xmlFree(szKey); //清空xmlChar类型
}
if(xmlHasProp(curNode, BAD_CAST"attribute") != 0){ //查看属性是attribute的子节点
propNodeptr = curNode; //如果没有就下一个字节点
}
curNode = curNode->next;
}
//查找属性
xmlAttrPtr attPtr = propNodeptr->properties;
while(attPtr != NULL){
if(!xmlStrcmp(attPtr->name, BAD_CAST"attribute")){
xmlChar *szAttr = xmlGetProp(propNodeptr, BAD_CAST"attribute");
printf("szAttr: %s\n", szAttr);
xmlFree(szAttr);
}
attPtr = attPtr->next;
}
xmlFreeDoc(doc);
return 0;
}
输出结果
文章来源地址https://www.toymoban.com/news/detail-712614.html
到了这里,关于以C之名,解析Xml的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!