【问题标题】:Multiple repeated sections in a config file配置文件中的多个重复部分
【发布时间】:2014-04-20 04:17:57
【问题描述】:

我有一个配置文件格式,我希望使用 Boost Program Options 来实现(因为我之前使用过那个库),但我必须以某种方式实现这样的块:

label = whatever
depth = 3

start
source = /etc
dest = /tmp/etc/
end

start
source = /usr/local/include
dest = /tmp/include
depth = 1
end

我读到in the docs 说我可以拥有[sections],所以我首先想知道这个:

label = whatever
depth = 3

[dir]
source = /etc
dest = /tmp/etc/

[dir]
source = /usr/local/include
dest = /tmp/include
depth = 1

但如果我理解正确,dir 将成为变量名的一部分,因此不可能重复,这也行不通。所以我想知道将source 移动为部分名称:

label = whatever
depth = 3

[/etc]
dest = /tmp/etc/

[/usr/local/include]
dest = /tmp/include
depth = 1

这看起来是一种合理的方法吗?我想知道当我事先不知道部分名称时如何遍历部分列表?

或者,有没有更好的方法来使用程序选项库来实现这一点?

【问题讨论】:

    标签: c++ boost boost-program-options


    【解决方案1】:

    也许您应该使用Boost property_tree 而不是program_options,因为您的文件格式似乎与Windows INI file format 非常相似。 Boost property_tree 有一个 parser for INI files (以及一个序列化器,以防你也需要它)。

    然后将通过遍历树来处理您的选项。不在一个节中的选项将位于树根下,而节选项将位于该节的节点下。

    如果你真的想,你可以使用program_options。关键是将true作为最后一个参数传递给parse_config_file,即allow_unregistered_options

    #include <iostream>
    #include <sstream>
    #include <boost/program_options.hpp>
    
    static const std::string fileData = // sample input
       "foo=1\n"
       "[bar]\n"
       "foo=a distinct foo\n"
       "[/etc]\n"
       "baz=all\n"
       "baz=multiple\n"
       "baz=values\n"
       "a.baz=appear\n";
    
    int main(int argc, char *argv[]) {
       namespace po = boost::program_options;
    
       std::istringstream is(fileData);
       po::parsed_options parsedOptions = po::parse_config_file(
          is,
          po::options_description(),
          true);                     // <== allow unregistered options
    
       // Print out results.
       for (const auto& option : parsedOptions.options) {
          std::cout << option.string_key << ':';
    
          // Option value is a vector of strings.
          for (const auto& value : option.value)
             std::cout << ' ' << value;
          std::cout << '\n';
       }
    
       return 0;
    }
    

    这个输出:

    $ ./po
    foo: 1
    bar.foo: a distinct foo
    /etc.baz: all
    /etc.baz: multiple
    /etc.baz: values
    /etc.baz: appear
    

    但是,请注意,使用这种方法得到的是一个选项向量,而不是 program_options 的典型使用产生的映射。因此,您最终可能会将 parsed_options 容器处理成您可以更轻松地查询的内容,并且该内容可能看起来像 property_tree

    这是一个使用property_tree 的类似程序。输入略有不同,因为property_tree 不允许重复键。

    #include <iostream>
    #include <sstream>
    #include <boost/property_tree/ptree.hpp>
    #include <boost/property_tree/ini_parser.hpp>
    
    static const std::string fileData = // sample input                             
       "foo=1\n"
       "[bar]\n"
       "foo=a distinct foo\n"
       "[/etc]\n"
       "foo=and another\n"
       "baz=all\n";
    
    static void print_recursive(
       const std::string& prefix,
       const boost::property_tree::ptree& ptree) {
       for (const auto& entry : ptree) {
          const std::string& key = entry.first;
          const boost::property_tree::ptree& value = entry.second;
          if (!value.data().empty())
             std::cout << prefix + key << ": " << value.data() << '\n';
          else
             print_recursive(prefix + key + '.', value);
       }
    }
    
    int main() {
       namespace pt = boost::property_tree;
    
       std::istringstream is(fileData);
       pt::ptree root;
       pt::read_ini(is, root);
    
       print_recursive("", root);
       return 0;
    }
    

    【讨论】:

    • 谢谢,听起来好像可以。当我熟悉程序选项时,我只是谨慎使用另一个库。那么我试图用程序选项库做的事情是不可能的吗? (我在我的问题中建议的 INI 样式格式主要是因为 Program Options 提供了......当然,它对大多数用户来说也很好并且可读)
    • @DarrenCook,请参阅编辑以使用 program_options 进行解析。不过,program_options 通常不是这样使用的,因此它可能与您之前的使用经验有所不同。
    • 谢谢。我想我会在您的示例数据上尝试 property_tree:namespace pt = boost::property_tree;pt::ptree settings;pt::read_ini(fileData,settings); 我收到一个运行时错误:“在抛出 'boost::exception_detail::clone_impl<:exception_detail::error_info_injector :property_tree::ini_parser::ini_parser_error> >'"
    • 不友好的错误(对我来说看起来像是有效的输入)是一种耻辱,因为我同意你的观点,PropertyTree 代码对于我正在尝试做的事情感觉更干净。
    • @DarrenCook 我认为你得到的例外只是因为property_tree 不允许重复键,因为program_options 这样做是在通知你语法错误。我在我的答案中添加了一个示例property_tree 程序,它大致做同样的事情。在实践中,您可能不需要遍历树,而只需在根上使用 get()get_optional() 进行查询。此外,递归遍历对于 .ini 文件来说是多余的,因为它们只允许两个级别(不像 property_tree 也支持的 JSON 和 XML)。
    【解决方案2】:

    不知道Boost Programs Options 库。但 INI 文件格式在 Win32 API 中具有内置支持。

    要在不知道其名称的情况下读取 INI 文件中的所有部分,您可以使用 MSDN Win32 GetPrivateProfileSectionNames

    要读取一个部分中的所有键,您可以使用MSDN Win32 GetPrivateProfileSection

    据说这个 API 只是为了兼容性,在后台它可以将 INI 文件键映射到注册表。

    在实践中它应该可以正常工作(它总是这样)。您甚至可以拥有 UNICODE INI 文件,假设它们具有 BOM 标记并且您调用 API 的 ..W 版本。

    配置文件格式的适用性及其位置(例如,是否可以存储在注册表中)取决于您的部署方案

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-04-10
      • 1970-01-01
      • 1970-01-01
      • 2018-08-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-12
      相关资源
      最近更新 更多