当前位置: 首页 > 知识库问答 >
问题:

使用Boost::Spirit解析异构数据

南门鸿振
2023-03-14

我正在想办法解决下面的问题。

我有以下格式的结构:

struct Data
{
     time_t timestamp;
     string id;
     boost::optional<int> data1;
     boost::optional<string> data2;
     // etc...
};

这应该以以下格式从单个行字符串中解析出来:

human_readable_timestamp;id;key1=value1 key2=value2.....

当然,键的顺序不必与结构中元素的顺序相匹配。

Boost::勇气适合这种类型的数据吗?我如何处理这个问题?我已经浏览了示例,但是我无法从示例中获得符合我要求的代码。

共有1个答案

萧懿轩
2023-03-14

您可以使用排列分析器。我在这里做了一个非常相似的例子:

  • 使用C和BOOST读取JSON文件

如果你有重复键,那么使用Kleene*更有意义,也许

  1. 使用语义操作来分配属性 /or/
  2. 使用属性自定义点分配结果
  3. 附言。还可以查看精神库中的关键字解析器(使用函数提升气组成规则)

如果你不希望使用语义动作(提振精神:“语义动作是邪恶的”?)当对< code>data元素使用排列时,您可以稍微调整该结构,使其与自动合成的属性类型相匹配:

struct Data
{
    boost::posix_time::ptime timestamp;
    std::string id;
    struct Fields {
        boost::optional<int> data1;
        boost::optional<std::string> data2;
    } fields;
};

现在解析器可以是:

    timestamp = stream;

    text  = lexeme [ '"' >> *~char_('"') >> '"' ];
    data1 = "key1" >> lit('=') >> int_;
    data2 = "key2" >> lit('=') >> text;
    id    = lexeme [ *~char_(';') ];

    start = timestamp >> ';' >> id >> ';' >> (data1 ^ data2);

对于评论,让它“有弹性”。我最终改变了排列解析器,并采用了第一个编号的方法(带有语义动作的Kleene star方法)。

    id     = lexeme [ *~char_(';') ];

    auto data1 = bind(&Data::Fields::data1, _val);
    auto data2 = bind(&Data::Fields::data2, _val);

    other  = lexeme [ +(graph-'=') ] >> '=' >> (real_|int_|text);

    fields = *(
                ("key1" >> lit('=') >> int_) [ data1 = _1 ]
              | ("key2" >> lit('=') >> text) [ data2 = _1 ]
              | other
              );

    start  = timestamp >> ';' >> id >> -(';' >> fields);

这将更改以下方面:

>

  • 为了能够跳过“其他”字段,我需要为“其他”字段想出一个合理的语法:

    other  = lexeme [ +(graph-'=') ] >> '=' >> (real_|int_|text);
    

    (允许键由除=之外的任何非空白组成,后跟=,后跟数字(渴望)或文本)。

    我扩展了文本的概念,以支持流行的引用/转义方案:

    text   = lexeme [ 
                '"' >> *('\\' >> char_ | ~char_('"')) >> '"'
              | "'" >> *('\\' >> char_ | ~char_("'")) >> "'"
              | *graph 
           ];
    

    它允许重复相同的键(在这种情况下,它保留最后一个看到的值)。

    如果您想禁止无效值,请替换< code >

    我用一些具有挑战性的案例扩展了测试用例:

        2015-Jan-26 00:00:00;id
        2015-Jan-26 14:59:24;id;key2="value"
        2015-Jan-26 14:59:24;id;key2="value" key1=42
        2015-Jan-26 14:59:24;id;key2="value" key1=42 something=awful __=4.74e-10 blarg;{blo;bloop='whatever \'ignor\'ed' key2="new} \"value\""
        2015-Jan-26 14:59:24.123;id;key1=42 key2="value" 
    

    现在打印出来了

    ----------------------------------------
    Parsing '2015-Jan-26 00:00:00;id'
    Parsing success
    2015-Jan-26 00:00:00    id
    data1: --
    data2: --
    ----------------------------------------
    Parsing '2015-Jan-26 14:59:24;id;key2="value"'
    Parsing success
    2015-Jan-26 14:59:24    id
    data1: --
    data2:  value
    ----------------------------------------
    Parsing '2015-Jan-26 14:59:24;id;key2="value" key1=42'
    Parsing success
    2015-Jan-26 14:59:24    id
    data1:  42
    data2:  value
    ----------------------------------------
    Parsing '2015-Jan-26 14:59:24;id;key2="value" key1=42 something=awful __=4.74e-10 blarg;{blo;bloop='whatever \'ignor\'ed' key2="new} \"value\""'
    Parsing success
    2015-Jan-26 14:59:24    id
    data1:  42
    data2:  new} "value"
    ----------------------------------------
    Parsing '2015-Jan-26 14:59:24.123;id;key1=42 key2="value" '
    Parsing success
    2015-Jan-26 14:59:24.123000 id
    data1:  42
    data2:  value
    

    在Coliru上直播

    //#define BOOST_SPIRIT_DEBUG
    #include <boost/optional/optional_io.hpp>
    #include <boost/date_time/posix_time/posix_time.hpp>
    #include <boost/date_time/posix_time/posix_time_io.hpp>
    #include <boost/fusion/adapted/struct.hpp>
    #include <boost/spirit/include/qi.hpp>
    #include <boost/spirit/include/phoenix.hpp>
    
    namespace qi = boost::spirit::qi;
    namespace phx = boost::phoenix;
    
    struct Data
    {
        boost::posix_time::ptime timestamp;
        std::string id;
        struct Fields {
            boost::optional<int> data1;
            boost::optional<std::string> data2;
        } fields;
    };
    
    BOOST_FUSION_ADAPT_STRUCT(Data::Fields,
            (boost::optional<int>, data1)
            (boost::optional<std::string>, data2)
        )
    
    BOOST_FUSION_ADAPT_STRUCT(Data,
            (boost::posix_time::ptime, timestamp)
            (std::string, id)
            (Data::Fields, fields)
        )
    
    template <typename It, typename Skipper = qi::space_type>
    struct grammar : qi::grammar<It, Data(), Skipper> {
        grammar() : grammar::base_type(start) {
            using namespace qi;
            timestamp = stream;
    
            real_parser<double, strict_real_policies<double> > real_;
    
            text   = lexeme [ 
                        '"' >> *('\\' >> char_ | ~char_('"')) >> '"'
                      | "'" >> *('\\' >> char_ | ~char_("'")) >> "'"
                      | *graph 
                   ];
    
            id     = lexeme [ *~char_(';') ];
    
            auto data1 = bind(&Data::Fields::data1, _val);
            auto data2 = bind(&Data::Fields::data2, _val);
    
            other  = lexeme [ +(graph-'=') ] >> '=' >> (real_|int_|text);
    
            fields = *(
                        ("key1" >> lit('=') >> int_) [ data1 = _1 ]
                      | ("key2" >> lit('=') >> text) [ data2 = _1 ]
                      | other
                      );
    
            start  = timestamp >> ';' >> id >> -(';' >> fields);
    
            BOOST_SPIRIT_DEBUG_NODES((timestamp)(id)(start)(text)(other)(fields))
        }
      private:
        qi::rule<It,                                 Skipper> other;
        qi::rule<It, std::string(),                  Skipper> text, id;
        qi::rule<It, boost::posix_time::ptime(),     Skipper> timestamp;
        qi::rule<It, Data::Fields(),                 Skipper> fields;
        qi::rule<It, Data(),                         Skipper> start;
    };
    
    int main() {
        using It = std::string::const_iterator;
        for (std::string const input : {
                "2015-Jan-26 00:00:00;id",
                "2015-Jan-26 14:59:24;id;key2=\"value\"",
                "2015-Jan-26 14:59:24;id;key2=\"value\" key1=42",
                "2015-Jan-26 14:59:24;id;key2=\"value\" key1=42 something=awful __=4.74e-10 blarg;{blo;bloop='whatever \\'ignor\\'ed' key2=\"new} \\\"value\\\"\"",
                "2015-Jan-26 14:59:24.123;id;key1=42 key2=\"value\" ",
                })
        {
            std::cout << "----------------------------------------\nParsing '" << input << "'\n";
            It f(input.begin()), l(input.end());
            Data parsed;
            bool ok = qi::phrase_parse(f,l,grammar<It>(),qi::space,parsed);
    
            if (ok) {
                std::cout << "Parsing success\n";
                std::cout << parsed.timestamp << "\t" << parsed.id << "\n";
                std::cout << "data1: " << parsed.fields.data1 << "\n";
                std::cout << "data2: " << parsed.fields.data2 << "\n";
            } else {
                std::cout << "Parsing failed\n";
            }
    
            if (f!=l)
                std::cout << "Remaining unparsed: '" << std::string(f,l) << "'\n";
        }
    }
    

  •  类似资料:
    • Spirit 是移动 Web 开发规范,基于日常开发的沉淀,总结了字体、交互、性能等方面的最佳实践,是移动Web开发的指导标准。 移动开发规范概述 以下规范建议,均是Alloyteam在日常开发过程中总结提炼出的经验,规范具备较好的项目实践,强烈推荐使用 字体设置 使用无衬线字体 body {    font-family: "Helvetica Neue", Helvetica, STHeiTi

    • 运行此代码时会出现异常。我想解析url,它是一个json对象数组: 这是我的日志: 更新的logcat: 完整logcat:05-13 22:15:01.438:I/dalvikvm(968):threadid=3:对信号3作出反应05-13 22:15:01.598:I/dalvikvm(968):将堆栈跟踪写入“/数据/anr/跟踪。txt’05-13 22:15:01.718:D/grall

    • 我想解析大约5-10种不同的消息类型,它们共享一种通用格式(例如JSON),但每种类型都有需要验证的特定字段。每条消息最终都应该被解析成一个定制的类/结构,其类型不需要任何类型的转换(例如,一个字段是int而不是variant/tuple)。我认为解决这个问题有两种方法: > 为每个特定消息编写语法,处理消息格式(本例中为JSON样板)的验证并验证字段的内容,返回真正的自定义结构 编写一个语法,仅

    • 我写了一个在C语言中使用PCL和提升的库。它正在完美地构建。当我试图用C语言为它做一个包装器时 /CLR我得到了很多未解决的错误。首先,为什么Visual Studio在构建包装器时再次重建我的库?其次,为什么我得到了以下未解决的错误?:

    • 我正在尝试为包含提升::property_tree(

    • 我一直在尝试解析xsd文件,以便能够在本地生成jaxb文件。为了做到这一点,我在linux上使用了命令,而不使用命令本身的任何选项。 但即使是我也非常基本地使用它: 它给出了一个错误,如下所示: 错误发生定义见下文第11行。 即使我一直在使用的xsd模式也是一个非常通用和正式的模式,它会给出上面看到的错误。因为它是默认模式,不应该被改变,我不知道我需要做什么... 在一些轮胎之后,我还尝试使用中的