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

相关推荐
itachi-uchiha9 小时前
awk处理xml文件&&封装集合变量和调用
xml·shell·awk
武子康3 天前
Java-39 深入浅出 Spring - AOP切面增强 核心概念 通知类型 XML+注解方式 附代码
xml·java·大数据·开发语言·后端·spring
Ll13045252985 天前
基于 COM 的 XML 解析技术(MSXML) 的总结
xml
在代码的海洋中寻找亚特兰蒂斯5 天前
AJAX对于XML和JSON的处理
xml·ajax·json
BinField6 天前
ToolsSet之:XML工具
xml·windows·microsoft
SEO-狼术7 天前
Connect Directly to Oracle XML Data
xml·数据库·oracle
YSoup7 天前
2025年目前最新版本Android Studio自定义xml预览的屏幕分辨率
android·xml·android studio
abcnull8 天前
mybatis的mapper对应的xml写法
xml·sql·spring·mybatis·mapper
Blue桃之夭夭8 天前
HTML、XML、JSON 是什么?有什么区别?又是做什么的?
xml·html·json
小于村8 天前
pom.xml 文件中配置你项目中的外部 jar 包打包方式
xml·java·jar