【学习】在C++中使用TinyXML读写xml文件

前言

工作上需要在qt项目中使用c++读写xml文件,尝试了QDomDocumentQXmlStreamReader 等原生库后太过折磨,故有了此篇文章,使用开源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");
posted @ 2024-05-06 20:57  小拳头呀  阅读(167)  评论(0编辑  收藏  举报