XML读写

XML初识

一、背景

自己写的从网页获取内容的小工具,那个网页是涉及到一个模拟器的启动。启动模拟器需要等五分钟才能读取数据。如果每次读都启动模拟器等待,等待时间较久,我在想应该在硬盘中将已经启动过模拟器的标志位写到xml文件中。所以就涉及到了xml。

二、关于XML

2.1 查找资料

概念

TinyXML是一个开源的解析XML的解析库。解析库的模型通过解析XML文件,在内存中生成DOM模型,使得可以方便的遍历XML。

官方下载地址:https://sourceforge.net/projects/tinyxml/?source=directory

github下载地址:https://github.com/leethomason/tinyxml2

模型

主要有两大模型:SAX和DOM。

SAX:基于事件的模型,基本工作流程是分析XML文档,当发现了一个新的元素时,产生一个对应事件,并调用相应的用户处理函数。这种方式占用内存少,速度快,但用户程序相应得会比较复杂;

DOM:文档对象模型,分析整个XML文档,将整个文档分成多个元素(如书、章、节、段等),在内存中形成对应的树结构。同时,向用户提供一系列的接口来访问和编辑树结构。这种方式占用内存大,速度往往慢于SAX,但可以给用户提供一个面向对象的访问接口,对用户更为友好;

参考文章:c++ tinyXml介绍

2.2 下载xml库

使用的是tinyXml2的源码

在clion上实现xml读写,将tinyxml2-10.0.0版本tinyXML源码放在路径下,cmakelist指向tinyXML路径

2.3 编写代码

xml读写代码

cpp 复制代码
#include <iostream>
#include <string>
#include <ctime>
#include <sstream>
#include <iomanip>
#include <unistd.h>
#include "tinyxml2.h"

using namespace std;
using namespace tinyxml2;

std::string GetCurrentPath()
{
    char cwd[1024];
    if (getcwd(cwd, sizeof(cwd)) != nullptr) {
        std::cout << "Current working directory: " << cwd << std::endl;
    } else {
        std::cerr << "Error retrieving current directory!" << std::endl;
    }
    return cwd;
}

std::string GetCurrentTime()
{
    std::time_t now = std::time(nullptr);
    std::tm* ltm = std::localtime(&now);

    std::stringstream ss;
    ss << std::put_time(ltm, "%Y-%m-%d %H:%M:%S");
    return ss.str();
}

std::string GetXMLFilePath()
{
    std::string currentPath = GetCurrentPath();
    std::string xmlFilePath = currentPath.substr(0, currentPath.find_last_of("/") + 1) + "data/favorite_place.xml";

    return xmlFilePath;
}

bool ReadXmlFile(std::string xmlPath)
{

    XMLDocument xml_doc;
    if (xml_doc.LoadFile(xmlPath.c_str()) != XML_SUCCESS) {
        return false;
    }

    XMLElement* xml_root = xml_doc.RootElement();
    if (NULL == xml_root) {
        return false;
    }

    XMLNode* p_data_type_node = xml_root->FirstChild();
    while (p_data_type_node) {
        XMLElement* p_data_level_element = p_data_type_node->ToElement();
        const XMLAttribute* pAttr = p_data_level_element->FirstAttribute();     //不知道有啥用;2024年6月1日11:18:31更新 用处就是获取attribute

        std::string str_time = p_data_level_element->GetText();
        std::cout << "str_time: " << str_time << std::endl;

        p_data_type_node = p_data_type_node->NextSibling();
    }

    return true;
}

bool ReadXMLWayTwo(std::string xmlPath)
{
    XMLDocument doc;
    if (doc.LoadFile(xmlPath.c_str()) != XML_SUCCESS) {
        cout << "Error parsing XML file" << endl;
        return false;
    }

    XMLElement* root = doc.RootElement();
    if (root == nullptr) {
        cout << "Error: XML is empty or has no root element!" << endl;
        return false;
    }

    // 遍历子元素
    for (XMLElement* child = root->FirstChildElement(); child != nullptr; child = child->NextSiblingElement()) {
        const char* value = child->GetText();
        if (value == nullptr) {
            cout << "Error: XML element has no text" << endl;
        } else {
            cout << child->Name() << ": " << value << endl;
        }
    }

    return true;
}

/*
 * <?xml version="1.0"?>
 * <root>
 * <element attribute="value">Some text</element>
 * </root>
 */
bool WriteXML(std::string xmlPath)
{
    XMLDocument doc;
    doc.InsertEndChild(doc.NewDeclaration());

    XMLElement* root = doc.NewElement("root");
    doc.InsertEndChild(root);

    // 创建一个元素并添加到根元素
    XMLElement* element = doc.NewElement("element");
    element->SetText(GetCurrentTime().c_str());
    root->InsertEndChild(element);

    // 创建属性
//    XMLAttribute* attr = doc.NewAttribute("attribute", "value");
//    element->InsertAttribute(attr);

    // 保存到文件
    XMLError error = doc.SaveFile(xmlPath.c_str());
    if (error != XML_SUCCESS) {
        std::cout << "Error saving XML file" << std::endl;
        return 1;
    }

    return true;

}

int main()
{
    std::string xmlPath = GetXMLFilePath();

    WriteXML(xmlPath);

    ReadXmlFile(xmlPath);

    ReadXMLWayTwo(xmlPath);

    return 0;
}

cmakelist.txt

shell 复制代码
cmake_minimum_required(VERSION 3.16.5)

message("this is cmakelist log")
message(${CMAKE_CURRENT_SOURCE_DIR})

get_filename_component(ProjectId ${CMAKE_CURRENT_SOURCE_DIR} NAME)
message(${ProjectId})
message(${ProjectId})
message(NAME)
string(REPLACE " " "_" ProjectId ${ProjectId})
message(${ProjectId})
project(${ProjectId} CXX)

#添加宏定义Debug为CMAKE_BUILD_TYPE
SET(CMAKE_BUILD_TYPE "Debug")

set(CMAKE_CXX_STANDARD 17)

if (CMAKE_BUILD_TYPE STREQUAL Debug)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -D_DEBUG")
else ()
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -D_DEBUG")
endif ()

#添加头文件
#例如:include_directories(/usr/include/abc /usr/include/xxx)
#是将"/usr/include/abc"和"/usr/include/xxx"这两个目录添加至编译器的头文件搜索目录(两个目录用空格分隔)。
include_directories(../3rd/tinyxml2-10.0.0)
include_directories(./include/)

aux_source_directory(../3rd/tinyxml2-10.0.0 tinyxmlSrc)
aux_source_directory(./src Src)

#通过编译源文件来创建一个可执行文件,其中,name是生成的可执行文件的名称,source是创建可执行文件所需要的源文件。
#源文件可以使用aux_source_directory指令返回的变量(将源文件保存在这个变量中),也可以是指定的.cpp文件(注意路径)。
add_executable(${ProjectId}
        ${Src}
        ${tinyxmlSrc}
        main.cpp)

打印信息

shell 复制代码
str_time: 2024-05-31 10:11:43
element: 2024-05-31 10:11:43

2.4 看b站使用tinyXML

其实在写上面的代码的饿时候,参考了cityView的代码,但是cityView的代码用到的代码中是TiXmlDocument的写法,我百度了发现好像他用的是tinyXML,而我用的是TinyXML2,我尝试去软件库查tinyXML,但是查到的还是TinyXML2,感觉还是要哔哩哔哩找个视频看下。

2.4.1

看哔哩哔哩看到一个网址,哔哩哔哩视频网址

https://sourceforge.net/projects/tinyxml/files/latest/download

实现读XML方法

xml文件格式

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns="http://xxx.com">
	<str id="001">
		<str-test>2024-06-01 09:21:32</str-test>
		<str-test>111</str-test>
		<str-test>222</str-test>
	</str>
</root>

代码如下:

cpp 复制代码
void TinyXMLRead()
{
    TiXmlDocument* pDoc = new TiXmlDocument();

    pDoc->LoadFile("/data2/z30031397/myProjTest2/test_xml/data/testTinyXml.xml");
    TiXmlElement* pRoot = pDoc->RootElement();
    printf("%s\n", pRoot->Value());
    TiXmlElement* str = pRoot->FirstChildElement("str");
    printf("%s\n", str->Value());
    printf("%s\n", str->Attribute("id"));
    TiXmlElement* strTest = str->FirstChildElement("str-test");

    while(strTest)
    {
        printf("%s\n", strTest->GetText());
        strTest = strTest->NextSiblingElement("str-test");
    }

    delete pDoc;
}

2.5 看b站使用tinyXML2

b站视频网址

up主csdn帖子C++调用tinyxml2读写xml

代码

cpp 复制代码
using namespace std;
using namespace tinyxml2;

void createXml()
{
    //------------
    //声明要创建的xml文件
    //------------
    tinyxml2::XMLDocument xml;
    tinyxml2::XMLDeclaration* declaration = xml.NewDeclaration();
    xml.InsertFirstChild(declaration);

    //------------
    //创建根节点
    //------------
    tinyxml2::XMLElement* rootNode = xml.NewElement("computer");
    xml.InsertEndChild(rootNode);

    //------------
    //创建子节点
    //------------
    tinyxml2::XMLElement* root_1_NodeComputerMonitor = xml.NewElement("ComputerMonitor");
    tinyxml2::XMLElement* root_1_NodeKeyboard = xml.NewElement("Keyboard");
    tinyxml2::XMLElement* root_1_CPU = xml.NewElement("CPU");

    //------------
    //给Monitor子节点增加内容
    //------------
    tinyxml2::XMLText* text_root_1_NodeComputerMonitor = xml.NewText("SAMSUNG");
    root_1_NodeComputerMonitor->InsertFirstChild(text_root_1_NodeComputerMonitor);

    //------------
    //给Keyboard子节点增加子节点
    //------------
    tinyxml2::XMLElement* root_2_NodeKeyboardAttribute = xml.NewElement("KeyboardAttribute");
    //------------
    //给Keyboard子节点的子节点KeyboardAttribute增加内容
    //------------
    tinyxml2::XMLText* text_root_2_NodeKeyboardAttribute = xml.NewText("cherry Mechanical Keyboard");
    root_2_NodeKeyboardAttribute->InsertFirstChild(text_root_2_NodeKeyboardAttribute);

    //------------
    //给Keyboard子节点的子节点KeyboardAttribute插入root_1_NodeKeyboard
    //------------
    root_1_NodeKeyboard->InsertEndChild(root_2_NodeKeyboardAttribute);


    //------------
    //给CPU子节点增加内容
    //------------
    tinyxml2::XMLText* text_root_1_root_1_CPU = xml.NewText("intel");
    root_1_CPU->InsertFirstChild(text_root_1_root_1_CPU);


    //------------
    //给Monitor和CPU子节点增加属性
    //------------
    root_1_NodeComputerMonitor->SetAttribute("size", "15");
    root_1_CPU->SetAttribute("series", "XEON");

    
    //------------
    //构建xml树,根节点的下的子节点树
    //------------
    rootNode->InsertEndChild(root_1_NodeComputerMonitor);//childNodeStu是root的子节点
    rootNode->InsertEndChild(root_1_NodeKeyboard);
    rootNode->InsertEndChild(root_1_CPU);


    //------------
    //将xml保存到当前项目中
    //------------
    xml.SaveFile("computer.xml");
}

void deCodeXml()
{
    //------------
    //声明
    //------------
    tinyxml2::XMLDocument xml;

    //------------
    //导入xml文件
    //------------
    if (xml.LoadFile("computer.xml") != XML_SUCCESS)
    {
        return;
    }

    //------------
    //找到导入的xml的根节点
    //------------
    tinyxml2::XMLElement* rootNode = xml.RootElement();
    if (rootNode == NULL) {
        return;
    }

    //------------
    //读取第一层子节点信息并且打印在控制台上
    //------------
    tinyxml2::XMLElement* root_1_NodeComputerMonitor = rootNode->FirstChildElement("ComputerMonitor");
    std::string text_root_1_NodeComputerMonitor = root_1_NodeComputerMonitor->GetText();
    cout << "text_root_1_NodeComputerMonitor = " << text_root_1_NodeComputerMonitor.c_str()<<endl;

    //------------
    //读取第二层子节点信息并且打印在控制台上
    //------------
    tinyxml2::XMLElement* root_1_NodeKeyboard = rootNode->FirstChildElement("Keyboard");
    tinyxml2::XMLElement* root_2_NodeKeyboardAttribute = root_1_NodeKeyboard->FirstChildElement("KeyboardAttribute");
    std::string text_root_2_NodeKeyboardAttribute = root_2_NodeKeyboardAttribute->GetText();
    cout << "text_root_2_NodeKeyboardAttribute = " << text_root_2_NodeKeyboardAttribute.c_str() << endl;
}


int main()
{
	cout << "----------------------begin create xml-----------------------" << endl;
	createXml();
	cout << "----------------------finished create xml--------------------" << endl;



	cout << "-----------------------begin read xml------------------------" << endl;
	deCodeXml();
	cout << "-----------------------finished read xml---------------------" << endl;
    return 0;
}

生成的xml文件形式:

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<computer>
    <ComputerMonitor size="15">SAMSUNG</ComputerMonitor>
    <Keyboard>
        <KeyboardAttribute>cherry Mechanical Keyboard</KeyboardAttribute>
    </Keyboard>
    <CPU series="XEON">intel</CPU>
</computer>

感觉看了这个,感觉清楚多了,好像感觉基本理解xml了

然后刚才我还在纠结xml.SaveFile("computer.xml");是将文件保存到哪里了,还百度了一下,xml.Save("configure.xml")与xml.Save(Filename)的区别。解释是:前者xml文件的搜索路径默认是:进程路径,如果进程路径改变(比如不在exe路径运行,换一个路径运行),就会出问题。

然后我还是没搞懂,最后在cmake-build-debug中找到了computer.xml文件。所以这种写法的文件路径是可执行文件的路径。

2.6 照着上述代码写了自己需要的代码

代码如下

cpp 复制代码
bool ReadXmlFile(std::string xmlPath, std::string &time)
{

    XMLDocument xml_doc;
    if (xml_doc.LoadFile(xmlPath.c_str()) != XML_SUCCESS) {
        return false;
    }

    XMLElement* xml_root = xml_doc.RootElement();
    if (NULL == xml_root) {
        return false;
    }

    XMLElement* pRecordTime = xml_root->FirstChildElement("recordTime");
    XMLElement* pLastTime = pRecordTime->FirstChildElement("lastTime");
    std::string lastTime = pLastTime->GetText();
    std::cout << "lastTime:  " << lastTime << std::endl;

    return true;
}

bool WriteXML(std::string xmlPath, std::string time)
{
    XMLDocument doc;
    doc.InsertEndChild(doc.NewDeclaration());

    XMLElement* root = doc.NewElement("root");


    // 创建一个元素并添加到根元素
    XMLElement* recordNode = doc.NewElement("recordTime");


    XMLElement* lastTimeNode = doc.NewElement("lastTime");
    XMLText* text_time = doc.NewText(time.c_str());
    lastTimeNode->InsertEndChild(text_time);
    recordNode->InsertEndChild(lastTimeNode);

    root->InsertEndChild(recordNode);
    doc.InsertEndChild(root);

    // 保存到文件
    XMLError error = doc.SaveFile(xmlPath.c_str());
    if (error != XML_SUCCESS) {
        std::cout << "Error saving XML file" << std::endl;
        return false;
    }

    return true;
}

std::string GetXMLFilePath()
{
    std::string currentPath = GetCurrentPath();
    std::string xmlFilePath = currentPath.substr(0, currentPath.find_last_of("/") + 1) + "data/test.xml";
    return xmlFilePath;
}

int main()
{
    std::string xmlPath = GetXMLFilePath();
    WriteXML(xmlPath, GetCurrentTime());

    std::string time = "1900-1-1 00:00:00";
    xmlPath = GetXMLFilePath();
    ReadXmlFile(xmlPath, time);

    return 0;
}

生成xml文件形式:

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<root>
    <recordTime>
        <lastTime>2024-06-01 15:45:18</lastTime>
    </recordTime>
</root>

然后程序一直有问题,readXml的时候读取失败,我按重点放在了InsertEndChild和InsertFirstChild上,但是实际上最后一点一点增加xml内容,最后定位出是 XMLElement* lastTimeNode = doc.NewElement("last time");这一行,XMLElement不能是带空格的两个单词。晕!

三、其他文章

https://blog.csdn.net/anwh9295/article/details/127023055

相关推荐
小百菜1 天前
dom4j实现xml转map,xml转json字符串
xml·json·xml转map·xml转json
玄客)1 天前
XML标记语言
xml
 嘘 2 天前
文件操作:Xml转Excel
xml·python·excel
q567315232 天前
使用 Python 编辑 XML 文件中的文本字段
xml·java·数据库·python·sqlite
爱敲代码的小冰2 天前
java mapper 的 xml讲解
xml·java·mybatis
子非鱼9212 天前
【Ajax】原生Ajax与jQuery中的Ajax
xml·ajax·node.js·jquery
天天进步20153 天前
X2JS: XML与JSON的完美转换工具
xml·json
法迪3 天前
解读JobScheduler的jobs.xml
xml·功耗
残花月伴3 天前
配置文件格式(xml、properties、yml/yaml)
xml·java
zhangxiang_ouc3 天前
Caché/IRIS file2Xml 一个将文件转换成Studio导出的xml格式的工具
xml