C++ XML文件和解析

XML(可扩展标记语言)是一种用于存储和传输数据的标记语言。它具有自描述性和平台无关性的特点。XML 文档的格式主要由一组嵌套的元素和属性构成,结构清晰,易于理解和解析。

XML 文档的基本格式

一个 XML 文档通常包括以下部分:

  • XML 声明:标识文档和版本信息。
  • 根元素:整个 XML 文档只能有一个根元素,所有其他元素必须嵌套在根元素内。
  • 元素:具有开始标签和结束标签,可以嵌套其他元素。
  • 属性:为元素提供额外的信息。
  • 文本内容:元素可以包含文本内容。
  • 注释:用于注释文档内容。

具体示例

以下是一个简单的 XML 文档示例,展示了上述基本部分:

<?xml version="1.0" encoding="UTF-8"?>
<!-- 这是一个示例 XML 文档 -->
<bookstore>
  <book category="cooking">
    <title lang="en">Everyday Italian</title>
    <author>Giada De Laurentiis</author>
    <year>2005</year>
    <price>30.00</price>
  </book>
  <book category="children">
    <title lang="en">Harry Potter</title>
    <author>J K. Rowling</author>
    <year>2005</year>
    <price>29.99</price>
  </book>
  <book category="web">
    <title lang="en">Learning XML</title>
    <author>Erik T. Ray</author>
    <year>2003</year>
    <price>39.95</price>
  </book>
</bookstore>

XML 文档的详细部分

XML 声明

<?xml version="1.0" encoding="UTF-8"?>

这是文档的声明部分,指定了 XML 版本和编码方式。

注释

<!-- 这是一个示例 XML 文档 -->

注释可以放在 XML 文档中的任何位置,不会被解析器处理。

根元素

<bookstore>
    <!-- 所有内容都必须在根元素内部 -->
</bookstore>

bookstore 是根元素,所有其他元素都嵌套在其中。

元素

<book category="cooking">
    <title lang="en">Everyday Italian</title>
    <author>Giada De Laurentiis</author>
    <year>2005</year>
    <price>30.00</price>
</book>

book 是一个元素,其中包含了多个子元素和一个属性 category。

属性

<book category="cooking">
    <title lang="en">Everyday Italian</title>
    <!-- 其他子元素 -->
</book>

category 和 lang 是属性,为元素提供额外的信息。

文本内容

<title lang="en">Everyday Italian</title>

title 元素包含了文本内容 Everyday Italian。

常见的 XML 结构

嵌套元素

<family>
  <parent name="John">
    <child name="Doe" age="10"/>
    <child name="Jane" age="8"/>
  </parent>
</family>

元素可以嵌套其他元素,形成层级结构。

属性和子元素结合

<employee id="1001">
  <name>John Doe</name>
  <department>HR</department>
  <salary>5000</salary>
</employee>

元素可以同时包含属性和子元素。

自闭合元素

<img src="image.jpg" alt="Sample Image"/>

自闭合元素是一种简洁的表示方式,不包含子元素。

XML 文档的规则

  • 良好格式:XML 文档必须是良好格式的,即每个元素都必须正确关闭,元素必须正确嵌套,属性值必须用引号括起来。
  • 区分大小写:XML 标签是区分大小写的。
  • 根元素:每个 XML 文档必须有且只有一个根元素。
  • 特殊字符:某些字符(如 <, >, & 等)在 XML 中有特殊含义,需要使用转义序列(如 &lt;, &gt;, &amp;)表示。

XML文档解析

XML 文件解析通常有两种主要方式:DOM(Document Object Model)解析和 SAX(Simple API for XML)解析。这两种方法各有优缺点,适用于不同的应用场景。

DOM 解析

DOM 解析将整个 XML 文档读入内存中,并将其表示为一个树结构。每个节点在树中对应 XML 文档中的一个元素。用户可以通过 DOM API 访问和操作树中的节点。

优点
  • 易于使用:通过树结构可以方便地访问和操作 XML 文档中的任意节点。
  • 丰富的功能:支持随机访问和复杂的查询操作。
  • 标准化:DOM 是 W3C 标准,广泛支持和文档丰富。
缺点
  • 内存消耗大:需要将整个 XML 文档加载到内存中,对于大文件可能会导致高内存占用。
  • 性能较低:解析和构建整个文档树的开销较大,处理大文件时性能可能会成为瓶颈。
适用场景
  • 适用于需要频繁访问和操作 XML 文档的应用场景。
  • 适用于中小规模的 XML 文档。
示例(使用 TinyXML)
#include <tinyxml2.h>
using namespace tinyxml2;

int main() {
    XMLDocument doc;
    doc.LoadFile("example.xml");
    
    XMLElement* root = doc.RootElement();
    if (root != nullptr) {
        XMLElement* element = root->FirstChildElement("ElementName");
        if (element != nullptr) {
            const char* text = element->GetText();
            printf("Element text: %s\n", text);
        }
    }
    return 0;
}

SAX 解析

SAX 解析是一种事件驱动的解析方法,逐行读取 XML 文档,并在遇到不同的结构(如开始标签、结束标签、文本节点等)时触发相应的事件处理函数。SAX 解析不会将整个文档加载到内存中,而是按需处理文档内容。

优点
  • 内存消耗低:不需要将整个文档加载到内存中,适合处理大文件。
  • 性能高:按顺序处理文档内容,解析速度快。
缺点
  • 使用复杂:事件驱动的编程模型较为复杂,需要编写事件处理函数。
  • 不支持随机访问:只能按顺序读取文档内容,无法直接访问任意节点。
适用场景
  • 适用于处理大型 XML 文档或内存有限的环境。
  • 适用于一次性读取和处理文档内容的场景。
示例(使用 Expat)
#include <expat.h>
#include <cstdio>
#include <cstdlib>

void startElement(void *userData, const char *name, const char **atts) {
    printf("Start element: %s\n", name);
}

void endElement(void *userData, const char *name) {
    printf("End element: %s\n", name);
}

void characterData(void *userData, const char *s, int len) {
    printf("Character data: %.*s\n", len, s);
}

int main() {
    FILE *file = fopen("example.xml", "r");
    if (!file) return 1;

    XML_Parser parser = XML_ParserCreate(NULL);
    XML_SetElementHandler(parser, startElement, endElement);
    XML_SetCharacterDataHandler(parser, characterData);

    char buffer[1024];
    size_t len;
    while ((len = fread(buffer, 1, sizeof(buffer), file)) != 0) {
        if (XML_Parse(parser, buffer, len, feof(file)) == XML_STATUS_ERROR) {
            fprintf(stderr, "Parse error at line %lu:\n%s\n",
                XML_GetCurrentLineNumber(parser),
                XML_ErrorString(XML_GetErrorCode(parser)));
            return 1;
        }
    }
    XML_ParserFree(parser);
    fclose(file);
    return 0;
}

总结

  • DOM 解析:适合需要随机访问和操作 XML 内容的场景,使用简单但内存和性能消耗较大。
  • SAX 解析:适合处理大文件或内存有限的场景,性能高但使用复杂。

tinyxml2 库

tinyxml2 是一款简单、小巧、高效的开源C++ xml解析库,在 tinyxml2 库中,XMLNode 是一个基类,它有几个派生类型。这些派生类型用于表示不同类型的 XML 节点。以下是 XMLNode 的主要派生类型:

XMLDocument:

  • 表示整个 XML 文档。
  • XMLDocument 是 XMLNode 的根,可以包含其他节点。
  • 提供了加载、保存和解析 XML 文档的功能。

XMLElement:

  • 表示 XML 文档中的一个元素(节点)。
  • 可以包含其他元素、属性和文本。
  • 是最常用的节点类型,用于表示 XML 标签。

XMLText:

  • 表示 XML 元素中的文本内容。
  • 用于包含实际的文本数据。

XMLComment:

  • 表示 XML 文档中的注释节点。
  • 注释节点内容不参与 XML 文档的逻辑处理,只用于提供额外的信息。

XMLDeclaration:

  • 表示 XML 文档的声明部分(如 <?xml version="1.0" encoding="UTF-8"?>)。
  • 通常在文档的最开始。

XMLUnknown:

  • 表示未知的 XML 节点类型。
  • 用于处理不符合其他已知节点类型的节点。

XMLAttribute:

  • 严格来说,XMLAttribute 不是从 XMLNode 派生的,但它用于描述 XML 元素的属性。
  • 属性节点包含名称和值,不直接参与 XML 文档的树结构。

tinyxml2解析有限元求解文件

一些有限元求解器的求解文件就是xml格式的,以开源FEM软件FEBio的求解文件为例,我们使用tinyxml2来提取里面的一些关键信息。

对于一个包含2万节点,12万单元的模型文件,我们通过tinyxml2解节点和单元信息花了0.3s,这个效率还是不错的。

如下为tinyxml2解析FEBio求解文件源码:

#include "tinyxml2.h"

#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <sstream>
#include <chrono>

using namespace std;
using namespace tinyxml2;

class FemXmlParser
{
public:
	FemXmlParser(const char* xmlFile)
		:xml_file(xmlFile) {

	}

	void parse() {
		XMLDocument doc;
		if (doc.LoadFile(xml_file) != XML_SUCCESS) {
			std::cerr << "Failed to load file: " << xml_file << std::endl;
			return;
		}

		XMLElement* febio = doc.FirstChildElement("febio_spec");
		if (!febio) {
			std::cerr << "No <febio_spec> element in XML file." << std::endl;
			return;
		}

		XMLElement* module = febio->FirstChildElement("Module");
		if (module) {
			const char* type = module->Attribute("type");
			std::cout << "Module Type: " << (type ? type : "Unknown") << std::endl;
		}

		XMLElement* geometry = febio->FirstChildElement("Mesh");
		if (geometry) {
			XMLElement* nodesElement = geometry->FirstChildElement("Nodes");
			if (nodesElement) ParseNodes(nodesElement);

			XMLElement* elementsElement = geometry->FirstChildElement("Elements");
			if (elementsElement) ParseElements(elementsElement);
		}

		XMLElement* materialsElement = febio->FirstChildElement("Material");
		if (materialsElement) ParseMaterials(materialsElement);

		XMLElement* controlElement = febio->FirstChildElement("Control");
		if (controlElement) ParseControl(controlElement);
	}

	void printSumaryInfo()
	{
		std::cout << "Nodes: total " <<nodes.size()<< std::endl;
	
		std::cout << "Elements: total " << elements.size()<< std::endl;
		
		std::cout << "Materials:" << std::endl;
		for (const Material& material : materials) {
			std::cout << "ID: " << material.id << ", Type: " << material.type << std::endl;
		}

		std::cout << "Control:" << std::endl;
		std::cout << "Analysis Type: " << control.analysis << std::endl;
	}

	void printDetailInfo()
	{
		// 测试输出,确保数据正确存储在容器中
		std::cout << "Nodes:" << std::endl;
		for (const Node& node : nodes) {
			std::cout << "ID: " << node.id << ", Position: (" << node.x << ", " << node.y << ", " << node.z << ")" << std::endl;
		}

		std::cout << "Elements:" << std::endl;
		for (const Element& element : elements) {
			std::cout << "ID: " << element.id << ", Node IDs: ";
			for (int nodeId : element.nodeIds) {
				std::cout << nodeId << " ";
			}
			std::cout << std::endl;
		}

		std::cout << "Materials:" << std::endl;
		for (const Material& material : materials) {
			std::cout << "ID: " << material.id << ", Type: " << material.type << std::endl;
		}

		std::cout << "Control:" << std::endl;
		std::cout << "Analysis Type: " << control.analysis << std::endl;
	}

private:
	struct Node {
		int id;
		double x, y, z;
	};

	struct Element {
		int id;
		std::vector<int> nodeIds;
	};

	struct Material {
		int id;
		std::string type;
	};

	struct Control {
		std::string analysis;
	};

	vector<int> splitStringToInts(const string& str, char delimiter) {
		vector<int> result;
		size_t start = 0;
		size_t end = str.find(delimiter);

		while (end != string::npos) {
			result.push_back(stoi(str.substr(start, end - start)));
			start = end + 1;
			end = str.find(delimiter, start);
		}

		result.push_back(stoi(str.substr(start, end - start)));
		return result;
	}

	vector<double> splitStringToDoubles(const string& str, char delimiter) {
		vector<double> result;
		stringstream ss(str);
		string item;

		while (getline(ss, item, delimiter)) {
			result.push_back(stod(item));
		}

		return result;
	}

	void ParseNodes(XMLElement* nodesElement) {
		for (XMLElement* node = nodesElement->FirstChildElement("node"); node != nullptr; node = node->NextSiblingElement("node")) {
			Node n;
			node->QueryIntAttribute("id", &n.id);
			const char* nodeText = node->GetText();
			if (nodeText) {
				vector<double> ns = splitStringToDoubles(nodeText, ',');
				n.x = ns[0];
				n.y = ns[1];
				n.z = ns[2];
			}
			nodes.push_back(n);
		}
	}

	void ParseElements(XMLElement* elementsElement) {
		for (XMLElement* element = elementsElement->FirstChildElement("elem"); element != nullptr; element = element->NextSiblingElement("elem")) {
			Element e;
			element->QueryIntAttribute("id", &e.id);

			const char* elemText = element->GetText();
			if (elemText) {
				e.nodeIds = splitStringToInts(elemText, ',');
			}
			elements.push_back(e);
		}
	}

	void ParseMaterials(XMLElement* materialsElement) {
		for (XMLElement* material = materialsElement->FirstChildElement("material"); material != nullptr; material = material->NextSiblingElement("material")) {
			Material m;
			material->QueryIntAttribute("id", &m.id);
			m.type = material->Attribute("type");
			materials.push_back(m);
		}
	}

	void ParseControl(XMLElement* controlElement) {
		XMLElement* ctrl = controlElement->FirstChildElement("analysis");
		control.analysis = ctrl->GetText();
	}

private:
	const char* xml_file;
	std::vector<Node> nodes;
	std::vector<Element> elements;
	std::vector<Material> materials;
	Control control;
};


class Timer {
public:
	Timer() : start_time_point(std::chrono::high_resolution_clock::now()) {}

	void reset() {
		start_time_point = std::chrono::high_resolution_clock::now();
	}

	double elapsed() const {
		return std::chrono::duration_cast<std::chrono::microseconds>(
			std::chrono::high_resolution_clock::now() - start_time_point
			).count() / 1000.0;  // 返回毫秒
	}

	void report()
	{
		double elapsed_time = elapsed();
		std::cout << "Elapsed time: " << elapsed_time << " ms" << std::endl;
	}
private:
	std::chrono::high_resolution_clock::time_point start_time_point;
};

int main()
{
	Timer time;
	FemXmlParser parser("../big_file.xml");
	parser.parse();
	time.report();
	parser.printSumaryInfo();
	return 1;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/679581.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

React 中的 ForwardRef的使用

React 中的 forwardRef Hooks 是指将子组件的 Dom 节点暴露给给父组件&#xff0c;在 React 中如果想要访问 Dom 节点是通过 useRef 这个 hooks&#xff0c;而 forwardHook 在 useRef 做了扩展。useRef 是当前组件中间中的节点&#xff0c;而 forwardRef 相当于做了一层封装将父…

屏幕录制工具分享6款,附上详细电脑录屏教程(2024全新)

当你即将参加一个重要的在线会议或一堂关键的直播课&#xff0c;但又担心错过关键点或无法及时做笔记时&#xff0c;屏幕录制无疑是最好的方法之一。屏幕录制是一项非常有价值的技能&#xff0c;它能让你出于各种目的捕捉屏幕上的活动。无论你的目的是创建教程、演示软件功能、…

重学java 62.IO流 字节流 ③ 字节输入流

告别这种事情&#xff0c;没有道理可讲 —— 24.6.4 一、字节输入流的介绍以及方法的使用 1.概述: 字节输入流 InputStream,是一个抽象类 子类:FileInputStream 2.作用: 读数据,将数据从硬盘上读到内存中来 3.构造: FileInputstream(File file) FileInputstream(String path…

容器中运行ifconfig提示bash: ifconfig: command not found【笔记】

容器中运行ifconfig提示bash: ifconfig: command not found 这个问题是因为在容器中没有安装ifconfig命令。 在容器中安装ifconfig命令&#xff0c;可以使用以下命令&#xff1a; 对于基于Debian/Ubuntu的容器&#xff0c;使用以下命令&#xff1a; apt-get update apt-get …

Spring Boot 使用自定义注解和自定义线程池实现异步日志记录

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…

leetcode739 每日温度

题目 给定一个整数数组 temperatures &#xff0c;表示每天的温度&#xff0c;返回一个数组 answer &#xff0c;其中 answer[i] 是指对于第 i 天&#xff0c;下一个更高温度出现在几天后。如果气温在这之后都不会升高&#xff0c;请在该位置用 0 来代替。 示例 输入: tempe…

探秘Android系统启动的神秘面纱

系统启动过程对于任何操作系统来说都是一个非常关键的环节&#xff0c;Android作为移动设备领域中占据主导地位的操作系统&#xff0c;其启动过程也是个值得深入研究的重点话题。本文将为您解开Android启动过程的神秘面纱&#xff0c;详细剖析其中的每一个步骤&#xff0c;并通…

vue 将echart 下载为base64图片

1 echart是页面的子组件&#xff0c; 2 页面有多个echart 3 将多个echart下载为base64图片 // 子组件 echart&#xff0c;要保存echartconst chart this.$echarts.init(this.$refs.chart, light) this.chartData chart; //保存数据&#xff0c;供父组件alarmReport调用(th…

专业130+总分400+四川大学951信号与系统考研经验川大电子信息与通信工程,真题,大纲,参考书。教材。

今年四川大学951信号与系统专业课130&#xff08;据我所知没有140以上的今年&#xff09;&#xff0c;总分400&#xff0c;顺利上岸川大&#xff0c;回顾一下自己这一年的复习&#xff0c;希望自己的经历可以对大家复习有所借鉴&#xff0c;也是对自己的考研画上句话。专业课&a…

重庆耶非凡科技业务大盘点:这些领域你都了解吗?

重庆耶非凡科技有限公司&#xff0c;这家位于重庆市经开区的企业&#xff0c;以其独特的业务模式和专业的技术实力&#xff0c;赢得了业界的广泛认可。它的主要业务涵盖了选品师项目和人力RPO项目两大领域。 首先&#xff0c;我们不得不提的是耶非凡科技的选品师项目 在当今消费…

算法004:盛水最多的容器

这道题比较简单&#xff0c;使用双指针。 要求的是最大面积&#xff0c;对于一个水桶&#xff08;水杯来说&#xff09;&#xff0c;面积的算法是固定的&#xff0c;就是底乘以高。 在这个题中&#xff0c;我们把左边的位置设为left&#xff0c;右边的位置设为right&#xff…

vue3 + echarts 二次开发百分比饼图

效果图&#xff1a; 安装 pnpm i echarts 公共模块组件 <divclass"pie"ref"percent"style"width: 100%; height: calc(100% - 48px)"></div> import { ref, onMounted } from vue import * as echarts from echarts const prop…

JavaEE:http请求 | 过滤器 | 同步与异步请求 | 跨域问题 | axios框架 有这一篇就够!

&#x1f4c3;HTTP请求 ▐ http超文本传输协议&#xff1a; ⦁ http超文本传输协议属于应用层协议&#xff0c;传输内容必须是超文本内容 (网页内容) ⦁ 例如在网页上点击超链接&#xff0c;提交表单&#xff0c;都可以向后端发送一个http请求 ⦁ 一次http请求中包含请求行、…

盘点哪些企业容易被ddos攻击

DDoS&#xff08;分布式拒绝服务&#xff09;攻击已成为网络安全威胁中的重要一环。本文将探讨哪些类型的企业容易成为DDoS攻击的目标&#xff0c;并提出相应的防范策略&#xff0c;帮助企业更好地保护自身网络安全。 一、电子商务平台 电子商务平台作为线上交易和支付的重要场…

Direct local .aar file dependencies are not supported when building an AAR.

最近升级了最新的AndroidStdio版本&#xff0c;然后导入之前的安卓工程 然后经过一番折腾后项目可以跑了&#xff0c;但是意外发现出release包的时候报错了&#xff0c; Direct local .aar file dependencies are not supported when building an AAR. 网上有很多解决方法&am…

鸿蒙HarmonyOS实战—如何使用Video组件播放视频

1.视频播放 鸿蒙系统中&#xff0c;关于视频播放&#xff0c;可以使用上层视频组件Video。 参数如下 src 支持file:///data/storage路径前缀的字符串&#xff0c;用于读取应用沙箱路径内的资源。需要保证目录包路径下的文件有可读权限。 说明&#xff1a;视频支持的格式是&am…

opencv-python(四)

读取图像文件 image cv2.imread(path, flag) flag&#xff1a;1. 默认值&#xff0c;依原图像读取图像&#xff0c;保留Alpha透明度通道。2.IMREAD_GRAYSCALE&#xff1a;将图像转为灰度再读取。3.IMREAD_COLOR&#xff1a;将图像转为三通道BGR彩色再读取。 可读取的图像格…

猫头虎分享已解决Bug || Error: ‘fetch‘ is not defined

原创作者&#xff1a; 猫头虎 作者微信号&#xff1a; Libin9iOak 作者公众号&#xff1a; 猫头虎技术团队 更新日期&#xff1a; 2024年6月6日 博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &…

HBuildX创建uni-app项目

新建项目 输入项目名称&#xff0c;选择存放位置、项目模板、vue版本 创建成功后左边会显示项目目录 安装插件&#xff1a;工具-》插件安装&#xff0c;根据所选vue版本安装编译器 点击运行&#xff0c;选择你需要运行的地方即可

Java面试题:ArrayList底层实现原理、HashMap的实现原理、HashMap的jdk1.7和jdk1.8有什么区别

文章目录 一、List相关面试题1.1 ArrayList源码分析&#xff08;底层实现&#xff09;1.2 ArrayList底层的实现原理是什么1.3 ArrayList listnew ArrayList(10)中的list扩容几次1.4 如何实现数组和List之间的转换1.5 ArrayList 和 LinkedList 的区别是什么 二、HashMap相关面试…