我的输入是多个键值对,例如:
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' 最简单的解决方案是什么?
这是一种方法:
#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_)
主解析器的部分匹配单个字符,后跟等号字符,然后是有符号整数。等号表示为文字(即它相当于qi::lit('=')
解析器),这意味着它只匹配输入但不产生解析的数据。因此,这个解析器的结果是一个属性,它是两个元素的序列——一个字符和一个整数。
解析器的% ','
部分是一个列表解析器,它解析左侧解析器(即上面描述的解析器)描述的任意数量的输入片段,由右侧解析器描述的片段分隔(即用逗号字符在我们的例子中)。和以前一样,逗号字符是文字解析器,因此它不会产生输出。
该[inserter]
部分是一个语义动作,它是解析器每次匹配输入字符串的一部分时调用的函数。解析器将其所有解析输出作为第一个参数传递给该函数。在我们的例子中,语义动作附加到第 3 条中描述的解析器,这意味着传递了一个字符和一个整数的序列。Boost.Spirit 使用 afusion::vector
来传递这些数据。语义动作的其他两个参数在此示例中未使用,可以忽略。
本inserter
例中的函数是一个 lambda 函数,但它可以是任何其他类型的函数对象,包括常规函数、由 生成的函数std::bind
等。重要的部分是它具有指定的签名,并且它的第一个类型参数与解析器的属性兼容,它作为语义动作附加到该属性上。因此,如果我们在第 3 条中使用不同的解析器,则必须相应地更改此参数。
fusion::at_c< N >()
ininserter
获取向量在 index 处的元素N
。std::get< N >()
它与应用于时非常相似std::tuple
。