权双
发布于 2026-06-05 / 2 阅读
0

pugixml:轻量级的 C++ XML 处理库

pugixml 是一个轻量级的 C++ XML 处理库,具有极强的可移植性,易于集成和使用。

pugixml 是一个轻量级、高性能的 C++ XML 处理库,采用 DOM 模型(非流式解析),核心设计围绕 节点(node)属性(attribute)文本(text) 三大对象。

1 环境配置

1️⃣ 下载项目

项目地址:

👉GitHub - zeux/pugixml: Light-weight, simple and fast XML parser for C++ with XPath support · GitHub

git拉取:

git clone https://github.com/zeux/pugixml.git

2️⃣ 文件需求说明

无需编译,只需要src下的纯文本文件,目录结构如下:

src
└─ pugiconfig.hpp
└─ pugixml.cpp
└─ pugixml.hpp

无需 .lib,无需编译

实际在使用时,加载pugixml.hpp头文件即可。

#include "pugixml.hpp"

2 文档对象模型

pugixml以类似DOM的方式存储XML数据:整个XML文档(包括文档结构和元素数据)以树的形式存储在内存中。该树可以从字符流(文件、字符串、C++ I/O流)加载,然后通过特殊API或XPath表达式进行遍历。整个树是可变的:节点结构和节点/属性数据都可以随时更改。最后,文档转换的结果可以保存到字符流(文件、C++ I/O流或自定义传输)中。

树的根节点是文档本身,对应C++类型xml_document。文档拥有一个或多个子节点,这些子节点对应C++类型xml_node。节点具有不同类型;根据类型不同,一个节点可以包含一组子节点、一组属性(对应C++类型xml_attribute)以及一些额外数据(例如名称)。

❓📚🚧🐛

2.1 核心对象关系

graph LR
    xml_document[xml_document] -->|包含| xml_node[xml_node]
    xml_node -->|包含| xml_attribute[xml_attribute]
    xml_node -->|包含| xml_text[xml_text]
    xml_node -->|包含| 子节点
  • xml_document:根对象,代表整个 XML 文档。
  • xml_node:通用节点(元素、注释、文本等),90% 操作围绕它展开
  • xml_attribute:节点的属性(如 <node attr="value"> 中的 attr)。
  • xml_text:节点的文本内容(通过 .text() 获取)。

2.2 文档加载与保存

1️⃣ 加载 XML

pugi::xml_document doc;

// 从文件加载(自动检测编码)
pugi::xml_parse_result result = doc.load_file("file.xml"); 

// 检查错误(关键!)
if (!result) {
    std::cerr << "Parse error: " << result.description() 
              << " at offset " << result.offset << std::endl;
    return;
}

注意:

  • 必须检查 result!空文件/格式错误会返回 false,但不会抛出异常

2️⃣ 保存 XML

// 保存到文件(格式化/压缩)
doc.save_file("output.xml", "  ", 1); // "  "=缩进, 1=格式化(0=压缩)

// 保存到字符串
std::string buffer;
doc.save_string(buffer, nullptr, pugi::format_default);关键选项:
  • pugi::format_indent:缩进格式化(默认)。
  • pugi::format_no_declaration:不输出 <?xml ...?> 声明。
  • pugi::format_save_file_text:强制 UTF-8 编码(避免 BOM 问题)。

3️⃣基础遍历

// 获取根节点
pugi::xml_node root = doc.document_element();

// 遍历所有子节点(包括元素/文本/注释等)
for (pugi::xml_node child : root.children()) {
    if (child.type() == pugi::node_element) { // 过滤非元素节点
        std::string name = child.name();      // 元素名(如 "Website")
    }
}

// 按标签名遍历子节点(推荐!跳过非元素节点)
for (pugi::xml_node website : root.children("Website")) {
    // 直接处理 <Website> 元素
}

最佳实践:

  • 优先用 children("tag") 而非 children() + 手动过滤,避免误处理文本/注释节点
  • node.name() 返回 C 风格字符串(const char\*,比较时用 strcmp 或转 std::string

2.1.as_xxx() 系列方法详解

pugixml 为 xml_attributexml_text 提供了类型安全的转换方法,避免手动处理字符串。所有方法均自动处理空值(返回默认值),无需显式检查指针。

1. xml_attribute 的转换方法

表格

方法作用空属性/非法值时的返回值示例用法
.as_string()转换为 std::string""(空字符串)attr.as_string()"1"(若属性值为 id="1"
.as_int(int def = 0)转换为 intdef(默认 0)attr.as_int()1(若 id="1"
.as_uint(unsigned int def = 0)转换为无符号整型def(默认 0)attr.as_uint()4294967295(若 id="-1" 且平台为 32 位)
.as_double(double def = 0)转换为 doubledef(默认 0.0)attr.as_double()3.14(若 value="3.14"
.as_bool(bool def = false)转换为 booldef(默认 falseattr.as_bool()true(若 enabled="1""true"

关键特性:

  • 空属性处理:若属性不存在,直接返回默认值(如 as_int() 返回 0),无需额外检查
  • 类型转换逻辑:
    • 数字类型:按 C 风格解析(如 "123abc"123"abc"0)。
    • 布尔类型:仅当值为 "1", "true", "t", "y", "yes" 时返回 true,其余为 false
  • 安全边界:
    • 整型溢出时行为未定义(如 as_int("2147483648") 在 32 位系统可能返回负数)。
    • 需自定义校验时,应先获取字符串再手动解析(如用 std::stoi 捕获异常)。

2. xml_text 的转换方法(用于节点文本内容)

当通过 child("year").text() 获取 xml_text 对象时,使用相同方法:

1int year = website.child("year").text().as_int(); // ✅ 正确:text() 返回 xml_text

表格

方法作用空文本/非法值时的返回值
.as_string()转换为 std::string""
.as_int(int def = 0)转换为 intdef
.as_bool(bool def = false)转换为 booldef

xml_attribute 的区别:

  • 空值来源不同:

    • xml_attribute 空:属性不存在。
    • xml_text 空:节点无文本内容(如 <year></year>)。
  • 节点文本需通过 .text() 获取:

    1// 正确:child("year") 返回 xml_node → text() 返回 xml_text
    2website.child("year").text().as_int(); 
    3
    4// 错误:child("year") 是节点,不能直接调用 as_int()
    5website.child("year").as_int(); // ❌ 编译失败
    

3 示例

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

<Root>
    <Website id="1">
        <title>pgmpocket</title>
		<address>https://pgmpocket.com</address>
        <author>quanshuang</author>
        <year>2025</year>
    </Website>
    <!-- <Website id="2"> -->
        <!-- <title>Design Patterns</title> -->
        <!-- <author>Gang of Four</author> -->
        <!-- <year>1994</year> -->
    <!-- </Website> -->
</Root>
#include <iostream>
#include <string>
#include "pugixml.hpp"

#ifdef _WIN32
#include <shlwapi.h>
#pragma comment(lib, "shlwapi.lib")
#include <windows.h>
#else
#include <unistd.h>
#include <limits.h>
#endif


std::string get_executable_path()
{
#ifdef _WIN32
    char path[MAX_PATH];
    GetModuleFileNameA(nullptr, path, MAX_PATH);
    PathRemoveFileSpecA(path);
    return std::string(path);

#elif defined(__linux__)
    char path[PATH_MAX];
    ssize_t count = readlink("/proc/self/exe", path, PATH_MAX);
    if (count != -1) {
        path[count] = '\0';
        char* lastSlash = strrchr(path, '/');
        if (lastSlash) *lastSlash = '\0';
        return std::string(path);
    }
    return "";

#else
    return "";
#endif
}

int main()
{
    pugi::xml_document doc;

    // 加载 XML 文件
    pugi::xml_parse_result result = doc.load_file((get_executable_path() + "\\demo.xml").c_str());
    if (!result)
    {
        std::cout << "XML load fail: " << result.description() << std::endl;
        return 1;
    }

    pugi::xml_node root = doc.child("Root");
    if (!root) {
        return false;
    }

    // 遍历所有 book 节点
    for (pugi::xml_node website : root.children("Website"))
    {
        std::string id = website.attribute("id").as_string();
        std::string title = website.child("title").text().as_string();
        std::string address = website.child("address").text().as_string();
        std::string author = website.child("author").text().as_string();
        int year = website.child("year").text().as_int();

        std::cout << "Website ID : " << id << "\n"
            << "  Title   : " << title << "\n"
            << "  Address : " << address << "\n"
            << "  Author  : " << author << "\n"
            << "  Year    : " << year << "\n"
            << std::endl;
    }

    return 0;
}