解析键,键不唯一时的值对

时间:2018-11-27 16:49:10

标签: c++ boost qi

我的输入是多个键,值对,例如:

A=1, B=2, C=3, ..., A=4

我想将输入解析为以下类型:

 std::map< char, std::vector< int > > m

等号的值应附加到向量上。因此,解析后的输出应等于:

m['A']={1,4};
m['B']={2};
m['C']={3};

使用'boost :: spirit :: qi'最简单的解决方案是什么?

1 个答案:

答案 0 :(得分:1)

这是一种实现方法:

#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/vector.hpp>
#include <boost/fusion/include/at_c.hpp>
#include <iostream>
#include <utility>
#include <string>
#include <vector>
#include <map>

namespace qi = boost::spirit::qi;
namespace fusion = boost::fusion;

int main()
{
    std::string str = "A=1, B=2, C=3, A=4";

    std::map< char, std::vector< int > > m;
    auto inserter = [&m](fusion::vector< char, int > const& parsed,
        qi::unused_type, qi::unused_type)
    {
        m[fusion::at_c< 0 >(parsed)].push_back(fusion::at_c< 1 >(parsed));
    };

    auto it = str.begin(), end = str.end();
    bool res = qi::phrase_parse(it, end,
        ((qi::char_ >> '=' >> qi::int_)[inserter]) % ',',
        qi::space);

    if (res && it == end)
        std::cout << "Parsing complete" << std::endl;
    else
        std::cout << "Parsing incomplete" << std::endl;

    for (auto const& elem : m)
    {
        std::cout << "m['" << elem.first << "'] = {";
        for (auto value : elem.second)
            std::cout << " " << value;
        std::cout << " }" << std::endl;
    }

    return 0;
}

有关实现的一些评论:

  1. qi::phrase_parse是一种Boost.Spirit算法,它采用一对迭代器,一个解析器和一个跳过解析器,并在由迭代器表示的输入上运行解析器。在此过程中,它将更新开始的迭代器(在此示例中为it),以便它在返回时指向已用输入的末尾。返回的res值指示解析器是否成功(即,消费的输入可以成功解析)。 qi::phrase_parse还有其他形式可以提取属性(就Boost.Spirit而言,它是解析后的数据),但是我们这里不使用属性,因为您对生成的容器结构有特殊的要求。

  2. 跳过分析器用于跳过主分析器元素之间的部分输入。在这种情况下,qi::space表示输入中将忽略任何空格字符,例如可以类似地解析“ A = 1”和“ A = 1”。 qi::parse系列算法没有跳过解析器,因此需要主解析器处理所有输入而无需跳过。

  3. 主解析器的(qi::char_ >> '=' >> qi::int_)部分与single character匹配,后跟等号字符,后跟signed integer。等号用文字表示(即,它等效于qi::lit('=')解析器),这意味着它仅与输入匹配,而不会生成解析的数据。因此,此解析器的结果是一个属性,该属性是两个元素(一个字符和一个整数)的序列。

  4. 解析器的% ','部分是list parser,它解析由左侧解析器(即上述解析器)描述的任意数量的输入,并由右边的解析器描述的片段(在本例中为逗号)。和以前一样,逗号字符是文字​​解析器,因此不会产生输出。

  5. [inserter]部分是semantic action,此函数每次与输入字符串的一部分匹配时由解析器调用。解析器将所有解析后的输出作为此函数的第一个参数传递。在我们的例子中,语义动作附加在项目符号3中描述的解析器上,这意味着传递了一个字符序列和一个整数。 Boost.Spirit使用fusion::vector传递这些数据。语义动作的其他两个参数在此示例中未使用,可以忽略。

  6. 此示例中的inserter函数是lambda函数,但它可以是任何其他类型的函数对象,包括常规函数,由std::bind生成的函数等。重要的部分是它具有指定的签名,并且其第一个参数的类型与解析器的属性兼容,该解析器作为语义操作附加到该属性。因此,如果项目3中的解析器不同,则必须相应更改此参数。

  7. inserter中的
  8. fusion::at_c< N >()获得索引为N的向量元素。应用于std::get< N >()时,它与std::tuple非常相似。