【问题标题】:Boost Spirit - Skip unwanted lines among key value pairsBoost Spirit - 跳过键值对中不需要的行
【发布时间】:2016-02-23 18:31:00
【问题描述】:

我有一个键值对列表,由 EOL 分隔。 我让 Boost Spirit 为格式正确的行做我想做的事(即"MyKey : MyValue \r\n MyKey2 : MyValue2")。

现在我的问题是我想跳过不符合要求的行。 例如:

This is some title line! 
Key1:Value1
Some more gibberish to skip
Key2:Value2

我想出了以下我认为可行的代码,但结果映射为空并且解析失败。

  • 在我的KeyRule 中,我添加了“-qi::eol”以避免在遇到第一个KeyValue 分隔符之前耗尽无效行。
  • 在我的ItemRule 中,PairRule 都是可选的,eol 是 1 或更多以解决多个断线。

我阅读了以下主题: Why does parsing a blank line with Spirit produce an empty key value pair in map? 它通过自定义船长跳过注释行(以 # 开头),但在我的 在这种情况下,我想跳过任何不包含键值分隔符: 的行。 必须有一些优雅的东西。

#include <iostream>
#include <string>
#include <map>

#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/std_pair.hpp>

namespace qi = boost::spirit::qi;

template <typename Iterator, typename Skipper = qi::blank_type>
struct KeyValueParser : qi::grammar<Iterator, std::map<std::string, std::string>(), Skipper> {
    KeyValueParser() : KeyValueParser::base_type(ItemRule) {
        ItemRule = -PairRule >> *(+qi::eol >> -PairRule) >> -qi::eol;
        PairRule = KeyRule >> ':' >> ValueRule;
        KeyRule = qi::raw[+(qi::char_ - ':' - qi::eol)];
        ValueRule = qi::raw[+(qi::char_ - qi::eol)];
    }
    qi::rule<Iterator, std::map<std::string, std::string>(), Skipper> ItemRule;
    qi::rule<Iterator, std::pair<std::string, std::string>(), Skipper> PairRule;
    qi::rule<Iterator, std::string(), Skipper> KeyRule;
    qi::rule<Iterator, std::string(), Skipper> ValueRule;
};

int main() {
    const std::string input = " Line To Skip! \r\n  My Key : Value \r\n My2ndKey : Long    Value \r\n";

    std::string::const_iterator iter = input.begin(), end = input.end();

    KeyValueParser<std::string::const_iterator> parser;
    typedef std::map<std::string, std::string> MyMap;
    MyMap parsed_map;

    bool result = qi::phrase_parse(iter, end, parser, qi::blank, parsed_map);

    if (result && (iter == end)) {
        std::cout << "Success." << std::endl;
        for (MyMap::const_iterator pIter = parsed_map.begin(); pIter != parsed_map.end(); ++pIter) {
            std::cout << "\"" << pIter->first << "\" : \"" << pIter->second << "\"" << std::endl;
        }
    } else {
        std::cout << "Something failed. Unparsed: ->|" << std::string(iter, end) << "|<-" << std::endl;
    }

    getchar();
    return 0;
}

【问题讨论】:

    标签: c++ boost boost-spirit boost-spirit-qi


    【解决方案1】:

    我能想到的最优雅的方法是解析一个键值对 /optionally/,然后是任何乱码,直到行尾。

    你可以写:

    ItemRule  = -PairRule % (*~char_("\r\n") >> eol);
    

    唯一需要注意的是,在乱码行中,将插入“默认”对(空键和值),因此您必须删除该解析后。

    一种等效的编写方式(但不太优雅)是:

    ItemRule  = (hold[PairRule] | omit[ *~char_("\r\n") ]) % eol;
    

    演示

    这是一个完整的演示。注意我还把skipper知识移到了语法里面(对语法的正确操作很重要)。

    最后,我使用了 BOOST_SPIRIT_DEBUG 来打印调试输出。

    Live On Coliru

    #define BOOST_SPIRIT_DEBUG
    #include <boost/spirit/include/qi.hpp>
    #include <boost/fusion/include/std_pair.hpp>
    #include <map>
    
    namespace qi = boost::spirit::qi;
    
    template <typename Iterator>
    struct KeyValueParser : qi::grammar<Iterator, std::map<std::string, std::string>()> {
        KeyValueParser() : KeyValueParser::base_type(ItemRule) {
    
            using namespace qi;
    
            ItemRule  = skip(blank) [ -PairRule % (*~char_("\r\n") >> eol) ];
            ItemRule  = skip(blank) [ hold[PairRule] | omit[ *~char_("\r\n") ] ] % eol;
            PairRule  = KeyRule >> ':' >> ValueRule;
            KeyRule   = +~char_("\r\n:");
            ValueRule = +~char_("\r\n");
    
            BOOST_SPIRIT_DEBUG_NODES((ItemRule)(PairRule)(KeyRule)(ValueRule))
        }
      private:
        qi::rule<Iterator, std::map<std::string, std::string>()> ItemRule;
        qi::rule<Iterator, std::pair<std::string, std::string>(), qi::blank_type> PairRule;
        // lexemes
        qi::rule<Iterator, std::string()> KeyRule, ValueRule;
    };
    
    int main() {
        const std::string input = R"(
     Line To Skip! 
      My Key : Value 
    Some more gibberish to skip
     My2ndKey : Long    Value 
    )";
    
        std::string::const_iterator iter = input.begin(), end = input.end();
    
        KeyValueParser<std::string::const_iterator> parser;
        std::map<std::string, std::string> parsed_map;
    
        bool result = qi::parse(iter, end, parser, parsed_map);
    
        if (result && (iter == end)) {
            std::cout << "Success.\n";
    
            // drop empty lines:
            parsed_map.erase("");
    
            for (auto& p : parsed_map)
                std::cout << "\"" << p.first << "\" : \"" << p.second << "\"\n";
        } else {
            std::cout << "Something failed. Unparsed: ->|" << std::string(iter, end) << "|<-\n";
        }
    }
    

    打印

    Success.
    "My Key " : "Value "
    "My2ndKey " : "Long    Value "
    

    带有调试信息

    <ItemRule>
      <try>\n Line To Skip! \n  M</try>
      <PairRule>
        <try>\n Line To Skip! \n  M</try>
        <KeyRule>
          <try>\n Line To Skip! \n  M</try>
          <fail/>
        </KeyRule>
        <fail/>
      </PairRule>
      <PairRule>
        <try> Line To Skip! \n  My</try>
        <KeyRule>
          <try>Line To Skip! \n  My </try>
          <success>\n  My Key : Value \nS</success>
          <attributes>[[L, i, n, e,  , T, o,  , S, k, i, p, !,  ]]</attributes>
        </KeyRule>
        <fail/>
      </PairRule>
      <PairRule>
        <try>  My Key : Value \nSo</try>
        <KeyRule>
          <try>My Key : Value \nSome</try>
          <success>: Value \nSome more g</success>
          <attributes>[[M, y,  , K, e, y,  ]]</attributes>
        </KeyRule>
        <ValueRule>
          <try>Value \nSome more gib</try>
          <success>\nSome more gibberish</success>
          <attributes>[[V, a, l, u, e,  ]]</attributes>
        </ValueRule>
        <success>\nSome more gibberish</success>
        <attributes>[[[M, y,  , K, e, y,  ], [V, a, l, u, e,  ]]]</attributes>
      </PairRule>
      <PairRule>
        <try>Some more gibberish </try>
        <KeyRule>
          <try>Some more gibberish </try>
          <success>\n My2ndKey : Long   </success>
          <attributes>[[S, o, m, e,  , m, o, r, e,  , g, i, b, b, e, r, i, s, h,  , t, o,  , s, k, i, p]]</attributes>
        </KeyRule>
        <fail/>
      </PairRule>
      <PairRule>
        <try> My2ndKey : Long    </try>
        <KeyRule>
          <try>My2ndKey : Long    V</try>
          <success>: Long    Value \n</success>
          <attributes>[[M, y, 2, n, d, K, e, y,  ]]</attributes>
        </KeyRule>
        <ValueRule>
          <try>Long    Value \n</try>
          <success>\n</success>
          <attributes>[[L, o, n, g,  ,  ,  ,  , V, a, l, u, e,  ]]</attributes>
        </ValueRule>
        <success>\n</success>
        <attributes>[[[M, y, 2, n, d, K, e, y,  ], [L, o, n, g,  ,  ,  ,  , V, a, l, u, e,  ]]]</attributes>
      </PairRule>
      <PairRule>
        <try></try>
        <KeyRule>
          <try></try>
          <fail/>
        </KeyRule>
        <fail/>
      </PairRule>
      <success></success>
      <attributes>[[[[], []], [[M, y,  , K, e, y,  ], [V, a, l, u, e,  ]], [[M, y, 2, n, d, K, e, y,  ], [L, o, n, g,  ,  ,  ,  , V, a, l, u, e,  ]]]]</attributes>
    </ItemRule>
    

    【讨论】:

    • 这似乎有效,除了前导空格没有被修剪。我之前在键/值规则上使用了 qi::raw 和空白船长,但这似乎不再起作用。有什么想法吗?
    • @XanderLo 我不确定您的原始样本会如何修剪尾随空格(我认为没有)。我没看过那个。不过你可以使用这个装置:coliru.stacked-crooked.com/a/e47101d6968b40c4
    • 这是给我修剪前导空格的初始解决方案,它运行良好:coliru.stacked-crooked.com/a/7539f0ebf856e242 你的装置似乎工作,虽然有点复杂。
    • @XanderLo raw[*(a-b-c)] 的方式有点反直觉。它会在c 之前修剪空格,但不会在b 之前修剪空格。 raw[*(a-(b|c))] 将在 bc 之前修剪空格。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多