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
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不能是带空格的两个单词。晕!