【问题标题】:Boost property tree to parse custom configuration format提升属性树以解析自定义配置格式
【发布时间】:2021-05-27 23:26:21
【问题描述】:

按照@sehe 在这篇帖子Boost_option to parse a configuration file 中提供的这个链接,我需要解析可能有 cmets 的配置文件。

https://www.boost.org/doc/libs/1_76_0/doc/html/property_tree/parsers.html#property_tree.parsers.info_parser

但是既然有cmets(前导#),那么除了read_info()之外,是不是也应该用一个grammer_spirit来取出cmets呢?我指的是 /property_tree/examples 文件夹中的 info_grammar_spirit.cpp

【问题讨论】:

  • 密码是什么?你有输入样本吗?遇到什么错误?
  • 我修改了Boost安装路径中的示例代码。我刚才还注意到一些奇怪的事情:如果我使用 g++ 编译您的代码,则输出与 Visual Studio 的不同。例如,您原来的 get_child("Resnet50") 可以正常工作,无需修改它。这两个编译器有点混乱
  • 我认为在 ranged-for 中可能有一个合法的 UB 来源和一个临时的。当使用临时变量时,这往往会发生(例如(for (char ch : return_a_temporary_foo().member_returns_a_ref()) {} 是 UB)。如果不确定,请提取一个变量。UBSAN/ASAN 没有为 GCC 标记,因此它可能是一个 MSVC 错误

标签: c++ boost boost-propertytree


【解决方案1】:

你最好避免依赖于实现细节,所以我建议你预处理你的配置文件只是为了剥离 cmets。

"//" 简单地替换为"; " 可能就足够了。

基于上一个答案:

std::string tmp;
{
    std::ifstream ifs(file_name.c_str());
    tmp.assign(std::istreambuf_iterator<char>(ifs), {});
} // closes file

boost::algorithm::replace_all(tmp, "//", ";");
std::istringstream preprocessed(tmp);
read_info(preprocessed, pt);

现在,如果您将输入更改为包含 cmets:

Resnet50 {
    Layer CONV1 {
        Type: CONV // this is a comment
        Stride { X: 2, Y: 2 }       ; this too
        Dimensions { K: 64, C: 3, R: 7, S: 7, Y:224, X:224 }
    }

    // don't forget the CONV2_1_1 layer
    Layer CONV2_1_1 {
        Type: CONV
        Stride { X: 1, Y: 1 }       
        Dimensions { K: 64, C: 64, R: 1, S: 1, Y: 56, X: 56 }
    }
}

如果我们还扩展调试输出进行验证,它仍然可以按预期解析:

ptree const& resnet50 = pt.get_child("Resnet50");
for (auto& entry : resnet50) {
    std::cout << entry.first << " " << entry.second.get_value("") << "\n";

    std::cout << " --- Echoing the complete subtree:\n";
    write_info(std::cout, entry.second);
}

打印

Layer CONV1
 --- Echoing the complete subtree:
Type: CONV
Stride
{
    X: 2,
    Y: 2
}
Dimensions
{
    K: 64,
    C: 3,
    R: 7,
    S: 7,
    Y:224, X:224
}
Layer CONV2_1_1
 --- Echoing the complete subtree:
Type: CONV
Stride
{
    X: 1,
    Y: 1
}
Dimensions
{
    K: 64,
    C: 64,
    R: 1,
    S: 1,
    Y: 56,
    X: 56
}

Live On Coliru

是的,但是...?

如果 '//' 出现在字符串文字中怎么办?不会也换了吧。是的。

这不是图书馆质量的解决方案。您不应该期望它,因为您不必付出任何努力来解析您的定制配置文件格式。

您是唯一可以判断这种方法的缺点是否对您造成问题的一方。

但是,除了复制和修改 Boost 的解析器或从头开始实现自己的解析器之外,没有什么可以做的。

对于受虐狂

如果您不想重新实现整个解析器,但仍希望“智能”跳过字符串文字,这里有一个 pre_process 函数可以完成所有这些。这一次,是真正使用了Boost Spirit

#include <boost/spirit/home/x3.hpp>
std::string pre_process(std::string const& input) {
    std::string result;
    using namespace boost::spirit::x3;
    auto static string_literal
        = raw[ '"' >> *('\\'>> char_ | ~char_('"')) >> '"' ];

    auto static comment
        = char_(';') >> *~char_("\r\n")
        | "//" >> attr(';') >> *~char_("\r\n")
        | omit["/*" >> *(char_ - "*/") >> "*/"];

    auto static other
        = +(~char_(";\"") - "//" - "/*");

    auto static content
        = *(string_literal | comment | other) >> eoi;

    if (!parse(begin(input), end(input), content, result)) {
        throw std::invalid_argument("pre_process");
    }
    return result;
}

如您所见,它识别字符串文字(带有转义),它处理“//”和“;”将 linewise cmets 样式设置为等效。为了“炫耀”,我加入了 /block cmets/,它不能用正确的 INFO 语法表示,所以我们只是 omit[] 他们。

现在让我们用一个时髦的例子进行测试(从文档中的"Complicated example demonstrating all INFO features" 扩展):

#include <boost/property_tree/info_parser.hpp>
#include <iostream>
using boost::property_tree::ptree;

int main() {
    boost::property_tree::ptree pt;
    std::istringstream iss(
            pre_process(R"~~( ; A comment
key1 value1   // Another comment
key2 "value with /* no problem */ special // characters in it {};#\n\t\"\0"
{
   subkey "value split "\
          "over three"\
          "lines"
   {
      a_key_without_value ""
      "a key with special characters in it {};#\n\t\"\0" ""
      "" value    /* Empty key with a value */
      "" /*also empty value: */ ""       ; Empty key with empty value!
   }
})~~"));

    read_info(iss, pt);

    std::cout << " --- Echoing the parsed tree:\n";
    write_info(std::cout, pt);
}

打印 (Live On Coliru)

 --- Echoing the parsed tree:
key1 value1
key2 "value with /* no problem */ special // characters in it {};#\n    \"\0"
{
    subkey "value split over threelines"
    {
        a_key_without_value ""
        "a key with special characters in it {};#\n     \"\0" ""
        "" value
        "" ""
    }
}

【讨论】:

  • 哦。我误读了问题并将// 视为评论字符。我假设您可以进行更改以支持#-cmets(like so 是唯一复杂的情况)
  • 酷,我不知道如何编写文档中的“时髦示例”。现在我明白了 :-)。我不太了解 boost::spirit 部分的工作原理。
  • 没有人真正知道 :) Spirit 具有较高的学习曲线之一。但是一旦你精通了它,就很难抗拒再次使用它来编写繁琐的手动解析器:)
  • 是的,对 Spirit 仍然很模糊。 VS 由于某种原因无法识别 pt.get_value() ,这很烦人,因为 g++ 没有这样的问题。我将单独为 VS 开一个帖子。
猜你喜欢
  • 1970-01-01
  • 2023-03-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多