• C++ XML文件和解析


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

    XML 文档的基本格式

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

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

    具体示例

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

    1. "1.0" encoding="UTF-8"?>
    2. <bookstore>
    3. <book category="cooking">
    4. <title lang="en">Everyday Italiantitle>
    5. <author>Giada De Laurentiisauthor>
    6. <year>2005year>
    7. <price>30.00price>
    8. book>
    9. <book category="children">
    10. <title lang="en">Harry Pottertitle>
    11. <author>J K. Rowlingauthor>
    12. <year>2005year>
    13. <price>29.99price>
    14. book>
    15. <book category="web">
    16. <title lang="en">Learning XMLtitle>
    17. <author>Erik T. Rayauthor>
    18. <year>2003year>
    19. <price>39.95price>
    20. book>
    21. bookstore>

    XML 文档的详细部分

    XML 声明

    "1.0" encoding="UTF-8"?>

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

    注释

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

    根元素

    1. <bookstore>
    2. bookstore>

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

    元素

    1. <book category="cooking">
    2. <title lang="en">Everyday Italiantitle>
    3. <author>Giada De Laurentiisauthor>
    4. <year>2005year>
    5. <price>30.00price>
    6. book>

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

    属性

    1. <book category="cooking">
    2. <title lang="en">Everyday Italiantitle>
    3. book>

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

    文本内容

    <title lang="en">Everyday Italiantitle>

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

    常见的 XML 结构

    嵌套元素

    1. <family>
    2. <parent name="John">
    3. <child name="Doe" age="10"/>
    4. <child name="Jane" age="8"/>
    5. parent>
    6. family>

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

    属性和子元素结合

    1. <employee id="1001">
    2. <name>John Doename>
    3. <department>HRdepartment>
    4. <salary>5000salary>
    5. employee>

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

    自闭合元素

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

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

    XML 文档的规则

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

    XML文档解析

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

    DOM 解析

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

    优点
    • 易于使用:通过树结构可以方便地访问和操作 XML 文档中的任意节点。
    • 丰富的功能:支持随机访问和复杂的查询操作。
    • 标准化:DOM 是 W3C 标准,广泛支持和文档丰富。
    缺点
    • 内存消耗大:需要将整个 XML 文档加载到内存中,对于大文件可能会导致高内存占用。
    • 性能较低:解析和构建整个文档树的开销较大,处理大文件时性能可能会成为瓶颈。
    适用场景
    • 适用于需要频繁访问和操作 XML 文档的应用场景。
    • 适用于中小规模的 XML 文档。
    示例(使用 TinyXML)
    1. #include
    2. using namespace tinyxml2;
    3. int main() {
    4. XMLDocument doc;
    5. doc.LoadFile("example.xml");
    6. XMLElement* root = doc.RootElement();
    7. if (root != nullptr) {
    8. XMLElement* element = root->FirstChildElement("ElementName");
    9. if (element != nullptr) {
    10. const char* text = element->GetText();
    11. printf("Element text: %s\n", text);
    12. }
    13. }
    14. return 0;
    15. }

    SAX 解析

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

    优点
    • 内存消耗低:不需要将整个文档加载到内存中,适合处理大文件。
    • 性能高:按顺序处理文档内容,解析速度快。
    缺点
    • 使用复杂:事件驱动的编程模型较为复杂,需要编写事件处理函数。
    • 不支持随机访问:只能按顺序读取文档内容,无法直接访问任意节点。
    适用场景
    • 适用于处理大型 XML 文档或内存有限的环境。
    • 适用于一次性读取和处理文档内容的场景。
    示例(使用 Expat)
    1. #include
    2. #include
    3. #include
    4. void startElement(void *userData, const char *name, const char **atts) {
    5. printf("Start element: %s\n", name);
    6. }
    7. void endElement(void *userData, const char *name) {
    8. printf("End element: %s\n", name);
    9. }
    10. void characterData(void *userData, const char *s, int len) {
    11. printf("Character data: %.*s\n", len, s);
    12. }
    13. int main() {
    14. FILE *file = fopen("example.xml", "r");
    15. if (!file) return 1;
    16. XML_Parser parser = XML_ParserCreate(NULL);
    17. XML_SetElementHandler(parser, startElement, endElement);
    18. XML_SetCharacterDataHandler(parser, characterData);
    19. char buffer[1024];
    20. size_t len;
    21. while ((len = fread(buffer, 1, sizeof(buffer), file)) != 0) {
    22. if (XML_Parse(parser, buffer, len, feof(file)) == XML_STATUS_ERROR) {
    23. fprintf(stderr, "Parse error at line %lu:\n%s\n",
    24. XML_GetCurrentLineNumber(parser),
    25. XML_ErrorString(XML_GetErrorCode(parser)));
    26. return 1;
    27. }
    28. }
    29. XML_ParserFree(parser);
    30. fclose(file);
    31. return 0;
    32. }

    总结

    • 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 文档的声明部分(如 )。
    • 通常在文档的最开始。

    XMLUnknown:

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

    XMLAttribute:

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

    tinyxml2解析有限元求解文件

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

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

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

    1. #include "tinyxml2.h"
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. using namespace std;
    9. using namespace tinyxml2;
    10. class FemXmlParser
    11. {
    12. public:
    13. FemXmlParser(const char* xmlFile)
    14. :xml_file(xmlFile) {
    15. }
    16. void parse() {
    17. XMLDocument doc;
    18. if (doc.LoadFile(xml_file) != XML_SUCCESS) {
    19. std::cerr << "Failed to load file: " << xml_file << std::endl;
    20. return;
    21. }
    22. XMLElement* febio = doc.FirstChildElement("febio_spec");
    23. if (!febio) {
    24. std::cerr << "No element in XML file." << std::endl;
    25. return;
    26. }
    27. XMLElement* module = febio->FirstChildElement("Module");
    28. if (module) {
    29. const char* type = module->Attribute("type");
    30. std::cout << "Module Type: " << (type ? type : "Unknown") << std::endl;
    31. }
    32. XMLElement* geometry = febio->FirstChildElement("Mesh");
    33. if (geometry) {
    34. XMLElement* nodesElement = geometry->FirstChildElement("Nodes");
    35. if (nodesElement) ParseNodes(nodesElement);
    36. XMLElement* elementsElement = geometry->FirstChildElement("Elements");
    37. if (elementsElement) ParseElements(elementsElement);
    38. }
    39. XMLElement* materialsElement = febio->FirstChildElement("Material");
    40. if (materialsElement) ParseMaterials(materialsElement);
    41. XMLElement* controlElement = febio->FirstChildElement("Control");
    42. if (controlElement) ParseControl(controlElement);
    43. }
    44. void printSumaryInfo()
    45. {
    46. std::cout << "Nodes: total " <size()<< std::endl;
    47. std::cout << "Elements: total " << elements.size()<< std::endl;
    48. std::cout << "Materials:" << std::endl;
    49. for (const Material& material : materials) {
    50. std::cout << "ID: " << material.id << ", Type: " << material.type << std::endl;
    51. }
    52. std::cout << "Control:" << std::endl;
    53. std::cout << "Analysis Type: " << control.analysis << std::endl;
    54. }
    55. void printDetailInfo()
    56. {
    57. // 测试输出,确保数据正确存储在容器中
    58. std::cout << "Nodes:" << std::endl;
    59. for (const Node& node : nodes) {
    60. std::cout << "ID: " << node.id << ", Position: (" << node.x << ", " << node.y << ", " << node.z << ")" << std::endl;
    61. }
    62. std::cout << "Elements:" << std::endl;
    63. for (const Element& element : elements) {
    64. std::cout << "ID: " << element.id << ", Node IDs: ";
    65. for (int nodeId : element.nodeIds) {
    66. std::cout << nodeId << " ";
    67. }
    68. std::cout << std::endl;
    69. }
    70. std::cout << "Materials:" << std::endl;
    71. for (const Material& material : materials) {
    72. std::cout << "ID: " << material.id << ", Type: " << material.type << std::endl;
    73. }
    74. std::cout << "Control:" << std::endl;
    75. std::cout << "Analysis Type: " << control.analysis << std::endl;
    76. }
    77. private:
    78. struct Node {
    79. int id;
    80. double x, y, z;
    81. };
    82. struct Element {
    83. int id;
    84. std::vector<int> nodeIds;
    85. };
    86. struct Material {
    87. int id;
    88. std::string type;
    89. };
    90. struct Control {
    91. std::string analysis;
    92. };
    93. vector<int> splitStringToInts(const string& str, char delimiter) {
    94. vector<int> result;
    95. size_t start = 0;
    96. size_t end = str.find(delimiter);
    97. while (end != string::npos) {
    98. result.push_back(stoi(str.substr(start, end - start)));
    99. start = end + 1;
    100. end = str.find(delimiter, start);
    101. }
    102. result.push_back(stoi(str.substr(start, end - start)));
    103. return result;
    104. }
    105. vector<double> splitStringToDoubles(const string& str, char delimiter) {
    106. vector<double> result;
    107. stringstream ss(str);
    108. string item;
    109. while (getline(ss, item, delimiter)) {
    110. result.push_back(stod(item));
    111. }
    112. return result;
    113. }
    114. void ParseNodes(XMLElement* nodesElement) {
    115. for (XMLElement* node = nodesElement->FirstChildElement("node"); node != nullptr; node = node->NextSiblingElement("node")) {
    116. Node n;
    117. node->QueryIntAttribute("id", &n.id);
    118. const char* nodeText = node->GetText();
    119. if (nodeText) {
    120. vector<double> ns = splitStringToDoubles(nodeText, ',');
    121. n.x = ns[0];
    122. n.y = ns[1];
    123. n.z = ns[2];
    124. }
    125. nodes.push_back(n);
    126. }
    127. }
    128. void ParseElements(XMLElement* elementsElement) {
    129. for (XMLElement* element = elementsElement->FirstChildElement("elem"); element != nullptr; element = element->NextSiblingElement("elem")) {
    130. Element e;
    131. element->QueryIntAttribute("id", &e.id);
    132. const char* elemText = element->GetText();
    133. if (elemText) {
    134. e.nodeIds = splitStringToInts(elemText, ',');
    135. }
    136. elements.push_back(e);
    137. }
    138. }
    139. void ParseMaterials(XMLElement* materialsElement) {
    140. for (XMLElement* material = materialsElement->FirstChildElement("material"); material != nullptr; material = material->NextSiblingElement("material")) {
    141. Material m;
    142. material->QueryIntAttribute("id", &m.id);
    143. m.type = material->Attribute("type");
    144. materials.push_back(m);
    145. }
    146. }
    147. void ParseControl(XMLElement* controlElement) {
    148. XMLElement* ctrl = controlElement->FirstChildElement("analysis");
    149. control.analysis = ctrl->GetText();
    150. }
    151. private:
    152. const char* xml_file;
    153. std::vector nodes;
    154. std::vector elements;
    155. std::vector materials;
    156. Control control;
    157. };
    158. class Timer {
    159. public:
    160. Timer() : start_time_point(std::chrono::high_resolution_clock::now()) {}
    161. void reset() {
    162. start_time_point = std::chrono::high_resolution_clock::now();
    163. }
    164. double elapsed() const {
    165. return std::chrono::duration_cast(
    166. std::chrono::high_resolution_clock::now() - start_time_point
    167. ).count() / 1000.0; // 返回毫秒
    168. }
    169. void report()
    170. {
    171. double elapsed_time = elapsed();
    172. std::cout << "Elapsed time: " << elapsed_time << " ms" << std::endl;
    173. }
    174. private:
    175. std::chrono::high_resolution_clock::time_point start_time_point;
    176. };
    177. int main()
    178. {
    179. Timer time;
    180. FemXmlParser parser("../big_file.xml");
    181. parser.parse();
    182. time.report();
    183. parser.printSumaryInfo();
    184. return 1;
    185. }
  • 相关阅读:
    【JAVA】Servlet
    RCE极限挑战
    JAVA-分页查询
    不了解无线调制方式?这几个“老古董”大家现在还在用!
    QtCreator配置代码字体和颜色
    【iptables 实战】05 iptables设置网络转发实验
    P08 DQL
    76. 最小覆盖子串
    水果库存系统(SSM+Thymeleaf版)
    VulnHub Earth
  • 原文地址:https://blog.csdn.net/leizhengshenglzs/article/details/139455605