C++ Boost PropertyTree示例超详细讲解

软件发布|下载排行|最新软件

当前位置:首页IT学院IT技术

C++ Boost PropertyTree示例超详细讲解

无水先生   2022-11-09 我要评论

一、提要

借助类 boost::property_tree::ptree,Boost.PropertyTree 提供了一个树结构来存储键/值对。树形结构意味着一个树干存在许多分支,其中有许多树枝。文件系统是树结构的一个很好的例子。文件系统有一个带有子目录的根目录,这些子目录本身可以有子目录等等。

二、应用示例

要使用 boost::property_tree::ptree,请包含头文件 boost/property_tree/ptree.hpp。这是一个主头文件,因此 Boost.PropertyTree 不需要包含其他头文件。

示例 25.1。访问 boost::property_tree::ptree 中的数据

#include <boost/property_tree/ptree.hpp>
#include <iostream>
using boost::property_tree::ptree;
int main()
{
  ptree pt;
  pt.put("C:.Windows.System", "20 files");
  ptree &c = pt.get_child("C:");
  ptree &windows = c.get_child("Windows");
  ptree &system = windows.get_child("System");
  std::cout << system.get_value<std::string>() << '\n';
}

Example25.1

example25.1 使用 boost::property_tree::ptree 来存储目录的路径。这是通过调用 put() 来完成的。此成员函数需要两个参数,因为 boost::property_tree::ptree 是一个保存键/值对的树结构。树不仅由树枝和树枝组成,还必须为每个树枝和树枝分配一个值。在示例 25.1 中,该值为“20 个文件”。

传递给 put() 的第一个参数更有趣。它是一个目录的路径。但是,它不使用反斜杠,这是 Windows 上常见的路径分隔符。它使用点。

您需要使用点,因为它是 Boost.PropertyTree 期望的键的分隔符。参数“C:.Windows.System”告诉 pt 创建一个名为 C: 的分支,其中一个名为 Windows 的分支具有另一个名为 System 的分支。点创建分支的嵌套结构。如果“C:\Windows\System”作为参数传递,pt 将只有一个名为 C:\Windows\System 的分支。

调用 put() 后,访问 pt 以读取存储的值“20 个文件”并将其写入标准输出。这是通过从一个分支跳转到另一个分支 - 或从一个目录跳转到另一个目录来完成的。

要访问子分支,您可以调用 get_child(),它会返回对与调用 get_child() 相同类型的对象的引用。在示例 25.1 中,这是对 boost::property_tree::ptree 的引用。因为每个分支都可以有子分支,并且由于高低分支之间没有结构差异,所以使用相同的类型。

第三次调用 get_child() 检索 boost::property_tree::ptree,它表示目录 System。调用 get_value() 以读取在示例开头使用 put() 存储的值。

请注意,get_value() 是一个函数模板。您将返回值的类型作为模板参数传递。这样 get_value() 可以进行自动类型转换。

示例 25.2。访问 basic_ptree<std::string, int> 中的数据

#include <boost/property_tree/ptree.hpp>
#include <utility>
#include <iostream>
int main()
{
  typedef boost::property_tree::basic_ptree<std::string, int> ptree;
  ptree pt;
  pt.put(ptree::path_type{"C:\\Windows\\System", '\\'}, 20);
  pt.put(ptree::path_type{"C:\\Windows\\Cursors", '\\'}, 50);
  ptree &windows = pt.get_child(ptree::path_type{"C:\\Windows", '\\'});
  int files = 0;
  for (const std::pair<std::string, ptree> &p : windows)
    files += p.second.get_value<int>();
  std::cout << files << '\n';
}

与示例 25.1 相比,示例 25.2 有两个变化。这些更改是为了更轻松地保存目录路径和目录中的文件数量。首先,路径在传递给 put() 时使用反斜杠作为分隔符。其次,文件的数量存储为 int。

默认情况下,Boost.PropertyTree 使用点作为键的分隔符。如果您需要使用其他字符(例如反斜杠)作为分隔符,则不要将键作为字符串传递给 put()。相反,您将其包装在 boost::property_tree::ptree::path_type 类型的对象中。这个类的构造函数依赖于 boost::property_tree::ptree,它的第一个参数是键,第二个参数是分隔符。这样,您可以使用 C:\Windows\System 等路径,如示例 25.2 所示,而无需将反斜杠替换为点。

boost::property_tree::ptree 基于类模板 boost::property_tree::basic_ptree。因为键和值通常是字符串,所以 boost::property_tree::ptree 是预定义的。但是,您可以将 boost::property_tree::basic_ptree 用于键和值的不同类型。示例 25.2 中的树使用 int 来存储目录中的文件数,而不是字符串。

boost::property_tree::ptree 提供成员函数 begin() 和 end()。但是,boost::property_tree::ptree 只允许您在一个级别上迭代分支。示例 25.2 遍历 C:\Windows 的子目录。您无法让迭代器遍历所有级别的所有分支。

示例 25.2 中的 for 循环读取 C:\Windows 的所有子目录中的文件数以计算总数。因此,该示例显示 70。该示例不直接访问 ptree 类型的对象。相反,它迭代类型为 std::pair<std::string, ptree> 的元素。 first 包含当前分支的键。即示例 25.2 中的系统和游标。第二个提供对 ptree 类型对象的访问,它表示可能的子目录。在示例中,仅读取分配给 System 和 Cursors 的值。如示例 25.1,调用成员函数 get_value()。

boost::property_tree::ptree 只存储当前分支的值,而不是它的键。您可以使用 get_value() 获取值,但没有获取密钥的成员函数。密钥存储在 boost::property_tree::ptree 上一级。这也解释了为什么 for 循环会迭代 std::pair<std::string, ptree> 类型的元素。

示例 25.3。使用翻译器访问数据

#include <boost/property_tree/ptree.hpp>
#include <boost/optional.hpp>
#include <iostream>
#include <cstdlib>
struct string_to_int_translator
{
  typedef std::string internal_type;
  typedef int external_type;
  boost::optional<int> get_value(const std::string &s)
  {
    char *c;
    long l = std::strtol(s.c_str(), &c, 10);
    return boost::make_optional(c != s.c_str(), static_cast<int>(l));
  }
};
int main()
{
  typedef boost::property_tree::iptree ptree;
  ptree pt;
  pt.put(ptree::path_type{"C:\\Windows\\System", '\\'}, "20 files");
  pt.put(ptree::path_type{"C:\\Windows\\Cursors", '\\'}, "50 files");
  string_to_int_translator tr;
  int files =
    pt.get<int>(ptree::path_type{"c:\\windows\\system", '\\'}, tr) +
    pt.get<int>(ptree::path_type{"c:\\windows\\cursors", '\\'}, tr);
  std::cout << files << '\n';
}

Example25.3

示例 25.3 与 boost::property_tree::iptree 一起使用来自 Boost.PropertyTree 的另一个预定义树。通常,此类型的行为类似于 boost::property_tree::ptree。唯一的区别是 boost::property_tree::iptree 不区分大小写。例如,使用 C:\Windows\System 键存储的值可以用 c:\windows\system 读取。

与示例 25.1 不同,get_child() 不会被多次调用来访问子分支。正如 put() 可用于将值直接存储在子分支中一样,子分支中的值也可以使用 get() 读取。键的定义方式相同——例如使用 boost::property_tree::iptree::path_type。

与 get_value() 一样,get() 是一个函数模板。您必须将返回值的类型作为模板参数传递。 Boost.PropertyTree 进行自动类型转换。

为了转换类型,Boost.PropertyTree 使用翻译器。该库提供了一些开箱即用的翻译器,它们基于流并且可以自动转换类型。

示例 25.3 与 boost::property_tree::iptree 一起使用来自 Boost.PropertyTree 的另一个预定义树。通常,此类型的行为类似于 boost::property_tree::ptree。唯一的区别是 boost::property_tree::iptree 不区分大小写。例如,使用 C:\Windows\System 键存储的值可以用 c:\windows\system 读取。

与示例 25.1 不同,get_child() 不会被多次调用来访问子分支。正如 put() 可用于将值直接存储在子分支中一样,子分支中的值也可以使用 get() 读取。键的定义方式相同——例如使用 boost::property_tree::iptree::path_type。

与 get_value() 一样,get() 是一个函数模板。您必须将返回值的类型作为模板参数传递。 Boost.PropertyTree 进行自动类型转换。

为了转换类型,Boost.PropertyTree 使用翻译器。该库提供了一些开箱即用的翻译器,它们基于流并且可以自动转换类型。

Example25.3

#include <boost/property_tree/ptree.hpp>
#include <utility>
#include <iostream>
using boost::property_tree::ptree;
int main()
{
  ptree pt;
  pt.put("C:.Windows.System", "20 files");
  boost::optional<std::string> c = pt.get_optional<std::string>("C:");
  std::cout << std::boolalpha << c.is_initialized() << '\n';
  pt.put_child("D:.Program Files", ptree{"50 files"});
  pt.add_child("D:.Program Files", ptree{"60 files"});
  ptree d = pt.get_child("D:");
  for (const std::pair<std::string, ptree> &p : d)
    std::cout << p.second.get_value<std::string>() << '\n';
  boost::optional<ptree&> e = pt.get_child_optional("E:");
  std::cout << e.is_initialized() << '\n';
}

示例 25.3 定义了转换器 string_to_int_translator,它将 std::string 类型的值转换为 int。翻译器作为附加参数传递给 get()。因为翻译器只是用来阅读的,所以它只定义了一个成员函数,get_value()。如果您也想使用翻译器进行写作,那么您需要定义一个成员函数 put_value(),然后将翻译器作为附加参数传递给 put()。

get_value() 返回 pt 中使用的类型的值。但是,由于类型转换并不总是成功,因此使用了 boost::optional。如果示例 25.3 中存储的值无法使用 std::strtol() 转换为 int,则将返回 boost::optional 类型的空对象。

请注意,翻译人员还必须定义 internal_type 和 external_type 两种类型。如果需要在存储数据时进行类型转换,请定义类似于 get_value() 的 put_value()。

如果您修改示例 25.3 以存储值“20”而不是值“20 个文件”,则可以调用 get_value() 而无需传递翻译器。 Boost.PropertyTree 提供的翻译器可以将 std::string 转换为 int。但是,只有在可以转换整个字符串时,类型转换才会成功。字符串不能包含任何字母。因为只要字符串以数字开头,std::strtol() 就可以进行类型转换,因此示例 25.3 中使用了更自由的转换器 string_to_int_translator。

示例 25.4。 boost::property_tree::ptree 的各种成员函数

如果要读取键的值,可以调用成员函数 get_optional(),但不确定该键是否存在。 get_optional() 返回 boost::optional 类型对象中的值。如果未找到密钥,则该对象为空。否则,get_optional() 的工作方式与 get() 相同。

看起来 put_child() 和 add_child() 与 put() 相同。不同之处在于 put() 只创建一个键/值对,而 put_child() 和 add_child() 插入整个子树。请注意,类型为 boost::property_tree::ptree 的对象作为第二个参数传递给 put_child() 和 add_child()。

put_child() 和 add_child() 之间的区别在于 put_child() 会在该键已经存在时访问该键,而 add_child() 总是将一个新键插入到树中。这就是示例 25.4 中的树有两个名为“D:.Program Files”的键的原因。根据用例,这可能会令人困惑。如果一棵树代表一个文件系统,则不应有两条相同的路径。如果您不想在树中重复,则必须避免插入相同的键。

示例 25.4 显示了 for 循环中“D:”下方键的值。该示例将 50 个文件和 60 个文件写入标准输出,这证明有两个相同的键,称为“D:.Program Files”。

示例 25.4 中引入的最后一个成员函数是 get_child_optional()。此函数的使用方式与 get_child() 类似。 get_child_optional() 返回 boost::optional 类型的对象。如果您不确定密钥是否存在,则调用 boost::optional。

示例 25.5。以 JSON 格式序列化 boost::property_tree::ptree

#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <iostream>
using namespace boost::property_tree;
int main()
{
  ptree pt;
  pt.put("C:.Windows.System", "20 files");
  pt.put("C:.Windows.Cursors", "50 files");
  json_parser::write_json("file.json", pt);
  ptree pt2;
  json_parser::read_json("file.json", pt2);
  std::cout << std::boolalpha << (pt == pt2) << '\n';
}

Boost.PropertyTree 不仅仅提供结构来管理内存中的数据。从示例 25.5 中可以看出,该库还提供了将 boost::property_tree::ptree 保存在文件中并从文件中加载的函数。

头文件 boost/property_tree/json_parser.hpp 提供对函数 boost::property_tree::json_parser::write_json() 和 boost::property_tree::json_parser::read_json() 的访问。这些函数可以保存和加载以 JSON 格式序列化的 boost::property_tree::ptree。这样您就可以支持 JSON 格式的配置文件。

如果要调用将 boost::property_tree::ptree 存储在文件中或从文件中加载的函数,则必须包含头文件,例如 boost/property_tree/json_parser.hpp。仅包含 boost/property_tree/ptree.hpp 是不够的。

除了函数 boost::property_tree::json_parser::write_json() 和 boost::property_tree::json_parser::read_json() 之外,Boost.PropertyTree 还提供了其他数据格式的函数。您使用来自 boost/property_tree/ini_parser.hpp 的 boost::property_tree::ini_parser::write_ini() 和 boost::property_tree::ini_parser::read_ini() 来支持 INI 文件。使用来自 boost/property_tree/xml_parser.hpp 的 boost::property_tree::xml_parser::write_xml() 和 boost::property_tree::xml_parser::read_xml(),可以以 XML 格式加载和存储数据。使用来自 boost/property_tree/info_parser.hpp 的 boost::property_tree::info_parser::write_info() 和 boost::property_tree::info_parser::read_info(),您可以访问另一种为序列化 Boost 中的树而开发和优化的格式.PropertyTree。

任何受支持的格式都不能保证 boost::property_tree::ptree 在保存和重新加载后看起来是一样的。例如,JSON 格式可能会丢失类型信息,因为 boost::property_tree::ptree 无法区分 true 和“true”。类型始终相同。即使各种函数可以轻松保存和加载 boost::property_tree::ptree,但不要忘记 Boost.PropertyTree 并不完全支持这些格式。该库的主要重点是结构 boost::property_tree::ptree,而不是支持各种数据格式。

练习

创建一个加载此 JSON 文件并将所有动物的名称写入标准输出的程序。如果“all”设置为 true,则程序不仅应将所有动物的名称,而且应将所有属性写入标准输出:

{
  "animals": [
    {
      "name": "cat",
      "legs": 4,
      "has_tail": true
    },
    {
      "name": "spider",
      "legs": 8,
      "has_tail": false
    }
  ],
  "log": {
    "all": true
  }
}

Copyright 2022 版权所有 软件发布 访问手机版

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 联系我们