用精神解析文件输入
Parsing file input with spirit
我最近在玩 boost::spirit,想用它来解析文件输入。我得到的是:定义一些语义动作:
data = ifstream("herpderp", ios::in);
std::string line;
auto pri = [&](auto &ctx){cout << "got this" << endl;};
auto bri = [&](auto &ctx){cout << "got that" << endl;};
实际阅读是这样的:
while(getline(data, line, '\n'))
{
bool r = phrase_parse(line.begin(), line.end(), (int_ >> char_ >> int_ >> double_)[pri] | (int_ >> char_ >> int_)[bri], space);
}
但是问题是 - 我不知道如何访问 lambdas pri
和 bri
中 _attr(ctx)
的内容。我知道它们按预期工作,具体取决于文件的内容,因为 cout
打印(它们交替出现)——但是它们是复合类型,正如可以从解析规则中看出的那样。如果有人能对此有所了解,我将不胜感激。
编辑:
让它按照我想要的方式工作。它需要另一个导入
#include <boost/mpl/int.hpp>
每个 lambda 看起来像这样:
auto bri = [&](auto &ctx)
{
int firstIntFromMatch = at<boost::mpl::int_<0>>(_attr(ctx));
char charFromMatch = at<boost::mpl::int_<1>>(_attr(ctx));
int secondIntFromMatch = at<boost::mpl::int_<2>>(_attr(ctx));
doSomething(firstIntFromMatch, charFromMatch, secondIntFromMatch);
};
auto pri = [&](auto &ctx)
{
int firstIntFromMatch = at<boost::mpl::int_<0>>(_attr(ctx));
char charFromMatch = at<boost::mpl::int_<1>>(_attr(ctx));
int secondIntFromMatch = at<boost::mpl::int_<2>>(_attr(ctx));
double doubleFromMatch = at<boost::mpl::int_<3>>(_attr(ctx));
doSomething(firstIntFromMatch, charFromMatch, secondIntFromMatch);
doSomethingElse(doubleFromMatch);
};
我和@lakeweb 一起,另见
但是回答你的具体问题:属性是融合序列。包括 fusion/include/io.hpp
使您能够打印它们:
auto pri = [&](auto &ctx){std::cout << "got this: " << _attr(ctx) << std::endl;};
auto bri = [&](auto &ctx){std::cout << "got that: " << _attr(ctx) << std::endl;};
版画
got this: (321 a 321 3.14)
Parsed
got that: (432 b 432)
Parsed
做有用的事情
做有用的事情总是更令人兴奋。您可以 手动分解这些融合序列。定义我能想到的最简单的数据结构来接收我们的数据:
struct MyData {
int a = 0;
char b = 0;
int c = 0;
double d = 0;
friend std::ostream& operator<<(std::ostream& os, MyData const& md) {
return os << "MyData{" << md.a << "," << md.b << "," << md.c << "," << md.d << "}";
}
};
现在,我们可以"enhance"(阅读:复杂化)东西来解析它:
auto pri = [&](auto &ctx) {
auto& attr = _attr(ctx);
std::cout << "got this: " << attr << std::endl;
using boost::fusion::at_c;
_val(ctx) = { at_c<0>(attr), at_c<1>(attr), at_c<2>(attr), at_c<3>(attr) };
};
auto bri = [&](auto &ctx)
{
auto& attr = _attr(ctx);
std::cout << "got that: " << attr << std::endl;
using boost::fusion::at_c;
_val(ctx) = { at_c<0>(attr), at_c<1>(attr), at_c<2>(attr), std::numeric_limits<double>::infinity()};
};
auto const pri_rule = x3::rule<struct _pri, MyData> {"pri_rule"} =
(x3::int_ >> x3::char_ >> x3::int_ >> x3::double_)[pri];
auto const bri_rule = x3::rule<struct _bri, MyData> {"bri_rule"} =
(x3::int_ >> x3::char_ >> x3::int_)[bri];
是的,这个 "works":
for(std::string const line : {
"321 a 321 3.14",
"432 b 432"
})
{
MyData data;
bool r = x3::phrase_parse(
line.begin(), line.end(),
pri_rule | bri_rule,
x3::space,
data);
if (r)
std::cout << "Parsed " << data << "\n";
else
std::cout << "Failed\n";
}
版画
got this: (321 a 321 3.14)
Parsed MyData{321,a,321,3.14}
got that: (432 b 432)
Parsed MyData{432,b,432,inf}
然而这看起来非常复杂。
简化!!!
看来您只有一个可选的尾随 double_
。在一点帮助下:
BOOST_FUSION_ADAPT_STRUCT(MyData, a,b,c,d);
你可以在没有任何混乱的情况下获得同样的效果:
bool r = x3::phrase_parse(
line.begin(), line.end(),
x3::int_ >> x3::char_ >> x3::int_ >> (x3::double_ | x3::attr(9999)),
x3::space, data);
这将打印 Live On Coliru
Parsed MyData{321,a,321,3.14}
Parsed MyData{432,b,432,9999}
可选:可选性
如果您没有 double
的有效默认值,您可以将其设为可选:
x3::int_ >> x3::char_ >> x3::int_ >> -x3::double_,
并且仍然可以解析它:
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/adapted.hpp>
#include <boost/optional/optional_io.hpp>
#include <iostream>
namespace x3 = boost::spirit::x3;
struct MyData {
int a = 0;
char b = 0;
int c = 0;
boost::optional<double> d;
friend std::ostream& operator<<(std::ostream& os, MyData const& md) {
return os << "MyData{" << md.a << "," << md.b << "," << md.c << "," << md.d << "}";
}
};
BOOST_FUSION_ADAPT_STRUCT(MyData, a,b,c,d)
int main() {
for(std::string const line : { "321 a 321 3.14", "432 b 432" }) {
MyData data;
bool r = x3::phrase_parse(
line.begin(), line.end(),
x3::int_ >> x3::char_ >> x3::int_ >> -x3::double_,
x3::space, data);
if (r)
std::cout << "Parsed " << data << "\n";
else
std::cout << "Failed\n";
}
}
打印:
Parsed MyData{321,a,321, 3.14}
Parsed MyData{432,b,432,--}
我最近在玩 boost::spirit,想用它来解析文件输入。我得到的是:定义一些语义动作:
data = ifstream("herpderp", ios::in);
std::string line;
auto pri = [&](auto &ctx){cout << "got this" << endl;};
auto bri = [&](auto &ctx){cout << "got that" << endl;};
实际阅读是这样的:
while(getline(data, line, '\n'))
{
bool r = phrase_parse(line.begin(), line.end(), (int_ >> char_ >> int_ >> double_)[pri] | (int_ >> char_ >> int_)[bri], space);
}
但是问题是 - 我不知道如何访问 lambdas pri
和 bri
中 _attr(ctx)
的内容。我知道它们按预期工作,具体取决于文件的内容,因为 cout
打印(它们交替出现)——但是它们是复合类型,正如可以从解析规则中看出的那样。如果有人能对此有所了解,我将不胜感激。
编辑: 让它按照我想要的方式工作。它需要另一个导入
#include <boost/mpl/int.hpp>
每个 lambda 看起来像这样:
auto bri = [&](auto &ctx)
{
int firstIntFromMatch = at<boost::mpl::int_<0>>(_attr(ctx));
char charFromMatch = at<boost::mpl::int_<1>>(_attr(ctx));
int secondIntFromMatch = at<boost::mpl::int_<2>>(_attr(ctx));
doSomething(firstIntFromMatch, charFromMatch, secondIntFromMatch);
};
auto pri = [&](auto &ctx)
{
int firstIntFromMatch = at<boost::mpl::int_<0>>(_attr(ctx));
char charFromMatch = at<boost::mpl::int_<1>>(_attr(ctx));
int secondIntFromMatch = at<boost::mpl::int_<2>>(_attr(ctx));
double doubleFromMatch = at<boost::mpl::int_<3>>(_attr(ctx));
doSomething(firstIntFromMatch, charFromMatch, secondIntFromMatch);
doSomethingElse(doubleFromMatch);
};
我和@lakeweb 一起,另见
但是回答你的具体问题:属性是融合序列。包括 fusion/include/io.hpp
使您能够打印它们:
auto pri = [&](auto &ctx){std::cout << "got this: " << _attr(ctx) << std::endl;};
auto bri = [&](auto &ctx){std::cout << "got that: " << _attr(ctx) << std::endl;};
版画
got this: (321 a 321 3.14)
Parsed
got that: (432 b 432)
Parsed
做有用的事情
做有用的事情总是更令人兴奋。您可以 手动分解这些融合序列。定义我能想到的最简单的数据结构来接收我们的数据:
struct MyData {
int a = 0;
char b = 0;
int c = 0;
double d = 0;
friend std::ostream& operator<<(std::ostream& os, MyData const& md) {
return os << "MyData{" << md.a << "," << md.b << "," << md.c << "," << md.d << "}";
}
};
现在,我们可以"enhance"(阅读:复杂化)东西来解析它:
auto pri = [&](auto &ctx) {
auto& attr = _attr(ctx);
std::cout << "got this: " << attr << std::endl;
using boost::fusion::at_c;
_val(ctx) = { at_c<0>(attr), at_c<1>(attr), at_c<2>(attr), at_c<3>(attr) };
};
auto bri = [&](auto &ctx)
{
auto& attr = _attr(ctx);
std::cout << "got that: " << attr << std::endl;
using boost::fusion::at_c;
_val(ctx) = { at_c<0>(attr), at_c<1>(attr), at_c<2>(attr), std::numeric_limits<double>::infinity()};
};
auto const pri_rule = x3::rule<struct _pri, MyData> {"pri_rule"} =
(x3::int_ >> x3::char_ >> x3::int_ >> x3::double_)[pri];
auto const bri_rule = x3::rule<struct _bri, MyData> {"bri_rule"} =
(x3::int_ >> x3::char_ >> x3::int_)[bri];
是的,这个 "works":
for(std::string const line : {
"321 a 321 3.14",
"432 b 432"
})
{
MyData data;
bool r = x3::phrase_parse(
line.begin(), line.end(),
pri_rule | bri_rule,
x3::space,
data);
if (r)
std::cout << "Parsed " << data << "\n";
else
std::cout << "Failed\n";
}
版画
got this: (321 a 321 3.14)
Parsed MyData{321,a,321,3.14}
got that: (432 b 432)
Parsed MyData{432,b,432,inf}
然而这看起来非常复杂。
简化!!!
看来您只有一个可选的尾随 double_
。在一点帮助下:
BOOST_FUSION_ADAPT_STRUCT(MyData, a,b,c,d);
你可以在没有任何混乱的情况下获得同样的效果:
bool r = x3::phrase_parse(
line.begin(), line.end(),
x3::int_ >> x3::char_ >> x3::int_ >> (x3::double_ | x3::attr(9999)),
x3::space, data);
这将打印 Live On Coliru
Parsed MyData{321,a,321,3.14}
Parsed MyData{432,b,432,9999}
可选:可选性
如果您没有 double
的有效默认值,您可以将其设为可选:
x3::int_ >> x3::char_ >> x3::int_ >> -x3::double_,
并且仍然可以解析它:
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/adapted.hpp>
#include <boost/optional/optional_io.hpp>
#include <iostream>
namespace x3 = boost::spirit::x3;
struct MyData {
int a = 0;
char b = 0;
int c = 0;
boost::optional<double> d;
friend std::ostream& operator<<(std::ostream& os, MyData const& md) {
return os << "MyData{" << md.a << "," << md.b << "," << md.c << "," << md.d << "}";
}
};
BOOST_FUSION_ADAPT_STRUCT(MyData, a,b,c,d)
int main() {
for(std::string const line : { "321 a 321 3.14", "432 b 432" }) {
MyData data;
bool r = x3::phrase_parse(
line.begin(), line.end(),
x3::int_ >> x3::char_ >> x3::int_ >> -x3::double_,
x3::space, data);
if (r)
std::cout << "Parsed " << data << "\n";
else
std::cout << "Failed\n";
}
}
打印:
Parsed MyData{321,a,321, 3.14}
Parsed MyData{432,b,432,--}