我的输入是多个键,值对,例如:
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'最简单的解决方案是什么?
答案 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;
}
有关实现的一些评论:
qi::phrase_parse
是一种Boost.Spirit算法,它采用一对迭代器,一个解析器和一个跳过解析器,并在由迭代器表示的输入上运行解析器。在此过程中,它将更新开始的迭代器(在此示例中为it
),以便它在返回时指向已用输入的末尾。返回的res
值指示解析器是否成功(即,消费的输入可以成功解析)。 qi::phrase_parse
还有其他形式可以提取属性(就Boost.Spirit而言,它是解析后的数据),但是我们这里不使用属性,因为您对生成的容器结构有特殊的要求。
跳过分析器用于跳过主分析器元素之间的部分输入。在这种情况下,qi::space
表示输入中将忽略任何空格字符,例如可以类似地解析“ A = 1”和“ A = 1”。 qi::parse
系列算法没有跳过解析器,因此需要主解析器处理所有输入而无需跳过。
主解析器的(qi::char_ >> '=' >> qi::int_)
部分与single character匹配,后跟等号字符,后跟signed integer。等号用文字表示(即,它等效于qi::lit('=')
解析器),这意味着它仅与输入匹配,而不会生成解析的数据。因此,此解析器的结果是一个属性,该属性是两个元素(一个字符和一个整数)的序列。
解析器的% ','
部分是list parser,它解析由左侧解析器(即上述解析器)描述的任意数量的输入,并由右边的解析器描述的片段(在本例中为逗号)。和以前一样,逗号字符是文字解析器,因此不会产生输出。
[inserter]
部分是semantic action,此函数每次与输入字符串的一部分匹配时由解析器调用。解析器将所有解析后的输出作为此函数的第一个参数传递。在我们的例子中,语义动作附加在项目符号3中描述的解析器上,这意味着传递了一个字符序列和一个整数。 Boost.Spirit使用fusion::vector
传递这些数据。语义动作的其他两个参数在此示例中未使用,可以忽略。
此示例中的inserter
函数是lambda函数,但它可以是任何其他类型的函数对象,包括常规函数,由std::bind
生成的函数等。重要的部分是它具有指定的签名,并且其第一个参数的类型与解析器的属性兼容,该解析器作为语义操作附加到该属性。因此,如果项目3中的解析器不同,则必须相应更改此参数。
inserter
中的 fusion::at_c< N >()
获得索引为N
的向量元素。应用于std::get< N >()
时,它与std::tuple
非常相似。