【学习】在C++中使用TinyXML读写xml文件
前言
工作上需要在qt项目中使用c++读写xml文件,尝试了QDomDocument、QXmlStreamReader 等原生库后太过折磨,故有了此篇文章,使用开源xml解析库tinyxml读写xml。
仓库地址:https://github.com/leethomason/tinyxml2
文中示例xml:
<?xml version="1.0" encoding="UTF-8"?>
<preferences>
<general>
<RECENT_FILES>
<recent index="0" path="top.xdc"/>
<recent index="1" path="top.v"/>
<recent index="2" path="top.v"/>
<recent index="3" path="top.xdc"/>
</RECENT_FILES>
<RECENT_DIRECTORIES>
<recent index="0" path="C:/Users/DELL/Desktop"/>
<recent index="1" path="E:/WorkSpace/"/>
<recent index="2" path="E:/WorkSpace_a/"/>
<recent index="3" path="C:/Users/DELL"/>
</RECENT_DIRECTORIES>
<RECENT_PROJECTS>
<recent index="0" path="C:/Users/DELL/Desktop"/>
<recent index="1" path="E:/WorkSpace/"/>
<recent index="2" path="E:/WorkSpace_a/"/>
<recent index="3" path="C:/Users/DELL"/>
</RECENT_PROJECTS>
<RECENT_CHECKPOINTS/>
<RECENT_IP/>
</general>
</preferences>
读取
为什么一上来就是读取。相信有过开发经验的人一定非常理解,相较于创建这个需求,能够读、改、增更为重要。
读取这部分我会相对写的细一些,后续章节将省略这部分内容。
- 声明
- 这里eResult存储状态,后续的读文件、读值等操作的返回结果均存入该变量中。
tinyxml2::XMLDocument xml;
tinyxml2::XMLError eResult = xml.LoadFile("example.xml");
- 根据返回状态判断结果
if (eResult != tinyxml2::XML_SUCCESS){
// 导入失败
return;
}
- 获取根节点
- 读取前言中的示例文件,根节点为preferences
tinyxml2::XMLElement* rootNode = xml.RootElement();
if (rootNode == nullptr) {
return;
}
- 读取 general 节点信息
- 这多人性化。。
tinyxml2::XMLElement* generalNode = rootNode->FirstChildElement("general");
if (generalNode == nullptr) {
std::cout << "Failed to find 'general' element." << std::endl;
return ;
}
- 读取general节点下的 RECENT_FILES 节点信息
tinyxml2::XMLElement* recentFiles = generalNode->FirstChildElement("RECENT_FILES");
if (recentFiles == nullptr) {
std::cout << "Failed to find 'RECENT_FILES' element." << std::endl;
return ;
}
- 读取RECENT_FILES节点下的 recent 节点信息
- 主要展示一下循环遍历操作和获取指定属性值
tinyxml2::XMLElement* recentFile = recentFiles->FirstChildElement("recent");
while (recentFile != nullptr) {
int index;
const char* path;
// 尝试从当前recentFile存储的节点中读取index属性,存入index变量中
// 读取成功则返回tinyxml2::XML_SUCCESS
eResult = recentFile->QueryIntAttribute("index", &index);
if (eResult != tinyxml2::XML_SUCCESS) {
// 读取失败
std::cout << "Error reading index." << std::endl;
}
// 读取path属性,如果为空则读取失败
path = recentFile->Attribute("path");
if (path == nullptr) {
std::cout << "Error reading path." << std::endl;
}
std::cout << "Index: " << index << ", Path: " << path << std::endl;
// 查询下一个recent
recentFile = recentFile->NextSiblingElement("recent");
}
- 完整代码
//------------
//声明
//------------
tinyxml2::XMLDocument xml;
tinyxml2::XMLError eResult = xml.LoadFile("example.xml");
//------------
//导入xml文件
//------------
if (eResult != tinyxml2::XML_SUCCESS){
// 导入失败
return;
}
//------------
//找到导入的xml的根节点
//------------
tinyxml2::XMLElement* rootNode = xml.RootElement();
if (rootNode == nullptr) {
return;
}
//------------
//读取根节点下的 general 节点信息
//------------
tinyxml2::XMLElement* generalNode = rootNode->FirstChildElement("general");
if (generalNode == nullptr) {
std::cout << "Failed to find 'general' element." << std::endl;
return ;
}
//------------
//读取general节点下的 RECENT_FILES 节点信息
//------------
tinyxml2::XMLElement* recentFiles = generalNode->FirstChildElement("RECENT_FILES");
if (recentFiles == nullptr) {
std::cout << "Failed to find 'RECENT_FILES' element." << std::endl;
return ;
}
//------------
//读取RECENT_FILES节点下的 recent 节点信息
//------------
tinyxml2::XMLElement* recentFile = recentFiles->FirstChildElement("recent");
while (recentFile != nullptr) {
int index;
const char* path;
// 尝试从当前recentFile存储的节点中读取index属性,存入index变量中
// 读取成功则返回tinyxml2::XML_SUCCESS
eResult = recentFile->QueryIntAttribute("index", &index);
if (eResult != tinyxml2::XML_SUCCESS) {
// 读取失败
std::cout << "Error reading index." << std::endl;
}
// 读取path属性,如果为空则读取失败
path = recentFile->Attribute("path");
if (path == nullptr) {
std::cout << "Error reading path." << std::endl;
}
std::cout << "Index: " << index << ", Path: " << path << std::endl;
// 查询下一个recent
recentFile = recentFile->NextSiblingElement("recent");
}
更新
- 在读取代码的基础上进行修改即可,如将
<RECENT_FILES>
<recent index="0" path="top.xdc"/>
<recent index="1" path="top.v"/>
<recent index="2" path="top.v"/>
<recent index="3" path="top.xdc"/>
</RECENT_FILES>
- 修改为
<RECENT_FILES>
<recent index="0" path="top.xdc"/>
<recent index="1" path="top.v"/>
<recent index="2" path="xxxxxxx"/>
<recent index="3" path="top.xdc"/>
</RECENT_FILES>
- 遍历时修改:
while (recentFile != nullptr) {
int index;
const char* path;
eResult = recentFile->QueryIntAttribute("index", &index);
if (eResult != tinyxml2::XML_SUCCESS) {
std::cout << "Error reading index." << std::endl;
}
if (index == 2){
recentFile->SetAttribute("path","xxxxxxx");
if(xml.SaveFile(xmlPath) == 0)
return true;
}
// 查询下一个recent
recentFile = recentFile->NextSiblingElement("recent");
}
新增
- 新增是在指定节点上添加新的元素,如:
<RECENT_FILES>
<recent index="0" path="top.xdc"/>
<recent index="1" path="top.v"/>
<recent index="2" path="top.v"/>
<recent index="3" path="top.xdc"/>
</RECENT_FILES>
- 修改为
<RECENT_FILES>
<recent index="0" path="top.xdc"/>
<recent index="1" path="top.v"/>
<recent index="2" path="top.v"/>
<recent index="3" path="top.xdc"/>
<recent index="4" path="我是新增的标签"/>
</RECENT_FILES>
- 创建新的标签元素
// 创建一个新的 recent 元素并设置属性
tinyxml2::XMLElement* newRecent = xml.NewElement("recent");
newRecent->SetAttribute("index", "4");
newRecent->SetAttribute("path", "我是新增的标签");
- 加至recentFiles下(开动你们的小脑瓜,希望移除一个标签下的全部内容时,怎么改呢?)
tinyxml2::XMLElement* recentFiles = generalNode->FirstChildElement("RECENT_FILES");
// 添加新的 recent 元素到 RECENT_FILES 元素
recentFiles->InsertEndChild(newRecent);
- 完整代码
tinyxml2::XMLDocument xml;
tinyxml2::XMLError eResult = xml.LoadFile(xmlPath);
if(eResult != tinyxml2::XML_SUCCESS) {
std::cout << "load xml file failed" << std::endl;
return false;
}
//------------
//找到导入的xml的根节点
//------------
tinyxml2::XMLElement* rootNode = xml.RootElement();
if (rootNode == nullptr) {
return false;
}
//------------
//读取根节点下的 general 节点信息
//------------
tinyxml2::XMLElement* generalNode = rootNode->FirstChildElement("general");
if (generalNode == nullptr) {
std::cout << "Failed to find 'general' element." << std::endl;
return false;
}
//------------
//读取general节点下的 RECENT_FILES 节点信息
//------------
tinyxml2::XMLElement* recentFiles = generalNode->FirstChildElement("RECENT_FILES");
if (recentFiles == nullptr) {
std::cout << "Failed to find 'RECENT_FILES' element." << std::endl;
return false;
}
// 创建一个新的 recent 元素并设置属性
tinyxml2::XMLElement* newRecent = xml.NewElement("recent");
newRecent->SetAttribute("index", "4");
newRecent->SetAttribute("path", "我是新增的标签");
// 添加新的 recent 元素到 RECENT_FILES 元素
recentFiles->InsertEndChild(newRecent);
// 保存修改后的 XML 文件
eResult = xml.SaveFile(xmlPath);
if (eResult != tinyxml2::XML_SUCCESS) {
std::cout << "保存文件失败\n";
return false;
}
return true;
创建
正如前文所说,很多情况下使用tinyxml的目的是修改xml,原生或qtxml创建xml已经非常容易了。
- 这里简单实现创建一个xml文件,大家可利用上面的增加和更新模块尝试丰富它
<?xml version="1.0" encoding="UTF-8"?>
<preferences>
<general>
<RECENT_FILES>
</RECENT_FILES>
<RECENT_DIRECTORIES/>
<RECENT_PROJECTS/>
</general>
</preferences>
- 代码如下:
//------------
//声明要创建的xml文件
//------------
tinyxml2::XMLDocument xml;
tinyxml2::XMLDeclaration* declaration = xml.NewDeclaration();
xml.InsertFirstChild(declaration);
//------------
//创建根节点
//------------
tinyxml2::XMLElement* rootNode = xml.NewElement("preferences");
xml.InsertEndChild(rootNode);
//------------
//为 rootNode 插入 general 节点
//------------
tinyxml2::XMLElement* generalNode = xml.NewElement("general");
rootNode->InsertEndChild(generalNode);
//------------
//为 rootNode 插入 RECENT_FILES RECENT_DIRECTORIES RECENT_PROJECTS 节点
//------------
tinyxml2::XMLElement* recentFilesNode = xml.NewElement("RECENT_FILES");
tinyxml2::XMLElement* recentDirectionsNode = xml.NewElement("RECENT_DIRECTORIES");
tinyxml2::XMLElement* recentProjectsNode = xml.NewElement("RECENT_PROJECTS");
generalNode->InsertEndChild(recentFilesNode);
generalNode->InsertEndChild(recentDirectionsNode);
generalNode->InsertEndChild(recentProjectsNode);
//------------
//将xml保存到当前项目中
//------------
xml.SaveFile("xiaban.xml");