C++ Boost PropertyTree示例超详细讲解
一、提要
借助类 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 使用 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'; }
示例 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 使用翻译器。该库提供了一些开箱即用的翻译器,它们基于流并且可以自动转换类型。
#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 } }
到此这篇关于C++ Boost PropertyTree示例超详细讲解的文章就介绍到这了,更多相关C++ Boost PropertyTree内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
最新评论