我读了一个文件中的实数,用的是精神气。我尝试实现条件解析器,其中输入取决于行中的第一个字符。
#include <iostream>
#include <boost/fusion/adapted/struct/adapt_struct.hpp>
#include <boost/spirit/include/qi.hpp>
using namespace std;
namespace qi = boost::spirit::qi;
struct MyStruct {
double r1, r2, r3, r4;
double r5, r6, r7, r8;
};
BOOST_FUSION_ADAPT_STRUCT(
MyStruct,
(double, r1), (double, r2), (double, r3), (double, r4),
(double, r5), (double, r6), (double, r7), (double, r8)
);
int main(int argc, wchar_t* argv[])
{
string test =
"A+1.000000000000e+00+2.000000000000e+00+3.000000000000e+00+4.000000000000e+00\r\n"
"B+5.000000000000e+00+6.000000000000e+00+7.000000000000e+00+8.000000000000e+00\r\n";
qi::rule<string::const_iterator> CRLF = qi::copy(qi::lit("\r\n"));
qi::real_parser d19_12;
MyStruct ms;
qi::rule<string::const_iterator, MyStruct()> gr =
qi::lit("A") >> d19_12 >> d19_12 >> d19_12 >> d19_12 >> CRLF
>> (
(qi::lit('B') >> d19_12 >> d19_12 >> d19_12 >> d19_12 >> CRLF)
|
(qi::lit('C') >> d19_12 >> d19_12 >> d19_12 >> +qi::lit('_') >> qi::attr(0.0) >> CRLF)
)
;
string::const_iterator f = test.cbegin();
string::const_iterator e = test.cend();
bool ret = qi::parse(f, e, gr, ms);
return ret;
}如果没有“C”选项,所有东西都可以正常工作,但是添加这个选项会使解析器跳过值,结果是
预期结果是:
谢谢
发布于 2021-10-07 14:17:53
您可以调试规则。因此,简化"A+1+2+3+4\r\nB+5+6+7+8\r\n"的输入并将真正的解析器包装成规则,这就是调试输出:
住在Coliru
<gr>
<try>A+1+2+3+4\r\nB+5+6+7+8</try>
<d19_12>
<try>+1+2+3+4\r\nB+5+6+7+8\r</try>
<success>+2+3+4\r\nB+5+6+7+8\r\n</success>
<attributes>[1]</attributes>
</d19_12>
<d19_12>
<try>+2+3+4\r\nB+5+6+7+8\r\n</try>
<success>+3+4\r\nB+5+6+7+8\r\n</success>
<attributes>[2]</attributes>
</d19_12>
<d19_12>
<try>+3+4\r\nB+5+6+7+8\r\n</try>
<success>+4\r\nB+5+6+7+8\r\n</success>
<attributes>[3]</attributes>
</d19_12>
<d19_12>
<try>+4\r\nB+5+6+7+8\r\n</try>
<success>\r\nB+5+6+7+8\r\n</success>
<attributes>[4]</attributes>
</d19_12>
<CRLF>
<try>\r\nB+5+6+7+8\r\n</try>
<success>B+5+6+7+8\r\n</success>
<attributes>[]</attributes>
</CRLF>
<d19_12>
<try>+5+6+7+8\r\n</try>
<success>+6+7+8\r\n</success>
<attributes>[5]</attributes>
</d19_12>
<d19_12>
<try>+6+7+8\r\n</try>
<success>+7+8\r\n</success>
<attributes>[6]</attributes>
</d19_12>
<d19_12>
<try>+7+8\r\n</try>
<success>+8\r\n</success>
<attributes>[7]</attributes>
</d19_12>
<d19_12>
<try>+8\r\n</try>
<success>\r\n</success>
<attributes>[8]</attributes>
</d19_12>
<CRLF>
<try>\r\n</try>
<success></success>
<attributes>[]</attributes>
</CRLF>
<success></success>
<attributes>[[1, 2, 3, 4, 5, 4.27256e+180, 0, 0]]</attributes>
</gr>
Parsed: (1 2 3 4 5 4.27256e+180 0 0)事实上,它证实了所有的数字都被解析了。为什么属性传播没有做您期望的事情?
我的猜测是,它是属性传播,试图接受比您预期的多一点。问题是AST与规则不直接匹配:规则合成
tup4 := tuple<double, double, double, double>
attribute := tuple<tup4, variant<tup4, tup4> >在齐版本中,这确实被简化为tuple<tup4, tup4>,但是您的tup8实际上就像一个tup8,这是不一样的。因此,在传播时,规则只是做它认为是最好的选择,即分配第一个tup4。然后:耸耸肩:
修复
最简单的解决方法是使AST与规则相匹配。这可能非常有意义,因为更有可能的是,"A"、"B"、"C“都有语义意义。
namespace Ast {
struct A {
double r1, r2, r3, r4;
};
struct BC {
double r5, r6, r7, r8;
};
struct MyStruct {
A a;
BC bc;
};
using boost::fusion::operator<<;
} // namespace Ast调整它们:
BOOST_FUSION_ADAPT_STRUCT(Ast::A, r1, r2, r3, r4)
BOOST_FUSION_ADAPT_STRUCT(Ast::BC, r5, r6, r7, r8)
BOOST_FUSION_ADAPT_STRUCT(Ast::MyStruct, a, bc)请注意,没有进一步的更改,这只是确认了自动属性传播是一种基于启发式的方法:柯尔鲁:
Parsed: ((1 0 0 0) (2 0 0 0))(oops)。
使规则符合这一结构:
qi::rule<It> CRLF = "\r\n";
qi::rule<It, double> d19_12 = qi::double_;
qi::rule<It, Ast::A()> A = "A" >> d19_12 >> d19_12 >> d19_12 >> d19_12; //
qi::rule<It, Ast::BC()> BC = //
'B' >> d19_12 >> d19_12 >> d19_12 >> d19_12 | //
'C' >> d19_12 >> d19_12 >> d19_12 >> +qi::lit('_') >> qi::attr(0.0);
qi::rule<It, Ast::MyStruct()> gr = A >> CRLF >> BC >> CRLF;现在一切都成功了:柯尔鲁
打印
Parsed: ((1 2 3 4) (5 6 7 8))他山之石
在我看来,很多这些都是XY的问题。一个结构,有8个无特征的数字,可以有不同的含义似乎.不是你真正需要的。
此外,B/C的区别似乎表明你真的想要一个“可选数字”规则:
rule<It> CRLF = "\r\n";
rule<It, double()> d19_12 = raw[ //
double_[_val = _1] | //
omit[+char_("_")] //
][_pass = px::size(_1) == 19];
rule<It, Ast::Tup4()> Tup4 =
omit[char_("ABC")] >> d19_12 >> d19_12 >> d19_12 >> d19_12;请注意,“省略[char(”ABC“)]`_直接反映了我的直觉,即您在模型中丢弃语义信息。
现在语法变成了
rule<It, Ast::MyStruct()> gr = Tup4 >> CRLF >> Tup4 >> CRLF;实际上,它解析了完整的输入:柯尔鲁
Parsed: ((1.0001 2.0002 3.0003 4.0004) (5.0005 6.0006 7.0007 8.0008))简化!容器
事实上,我怀疑你可能会得到更好的服务,比如:
namespace Ast {
using Reals = boost::container::static_vector<double, 8>;
} // namespace Ast有趣的是,容器do享受更灵活的属性传播(使用新的警告)。你可以做一些直截了当的事情,比如:
qi::rule<It, Ast::Reals(char const*)> Line =
qi::omit[qi::char_(_r1)] >> d19_12 >> d19_12 >> d19_12 >> d19_12;
qi::rule<It, Ast::Reals()> gr = //
Line(+"A") >> CRLF >> Line(+"BC") >> CRLF;让我以这样一个活生生的例子结束:在编译器资源管理器上直播¹
//#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/container/static_vector.hpp>
#include <fmt/ranges.h>
#include <iomanip>
#include <iostream>
namespace qi = boost::spirit::qi;
namespace px = boost::phoenix;
namespace Ast {
using Reals = boost::container::static_vector<double, 8>;
} // namespace Ast
int main()
{
using It = std::string::const_iterator;
using namespace qi::labels;
qi::rule<It> CRLF = "\r\n";
qi::rule<It, double()> d19_12 = qi::raw[ //
qi::double_[_val = _1] | //
qi::omit[+qi::char_("_")] //
][_pass = px::size(_1) == 19];
qi::rule<It, Ast::Reals(char const*)> Line =
qi::omit[qi::char_(_r1)] >> d19_12 >> d19_12 >> d19_12 >> d19_12;
qi::rule<It, Ast::Reals()> gr = //
Line(+"A") >> CRLF >> Line(+"BC") >> CRLF;
BOOST_SPIRIT_DEBUG_NODES((gr)(Line)(d19_12)(CRLF))
for (std::string const test : {
"A+1.000100000000e+00+2.000200000000e+00+3.000300000000e+00+4.000400000000e+00\r\n"
"B+5.000500000000e+00+6.000600000000e+00+7.000700000000e+00+8.000800000000e+00\r\n",
"A+1.000100000000e+00+2.000200000000e+00+3.000300000000e+00+4.000400000000e+00\r\n"
"C+5.000500000000e+00+6.000600000000e+00+7.000700000000e+00___________________\r\n",
}) {
It f = test.cbegin(), e = test.cend();
Ast::Reals data;
if (parse(f, e, gr, data)) {
fmt::print("Parsed: {}\n", data);
} else {
fmt::print("Failed\n");
}
if (f != e) {
std::cout << "Remaining: " << std::quoted(std::string(f, e))
<< "\n";
}
}
}打印
Parsed: {1.0001, 2.0002, 3.0003, 4.0004, 5.0005, 6.0006, 7.0007, 8.0008}
Parsed: {1.0001, 2.0002, 3.0003, 4.0004, 5.0005, 6.0006, 7.0007, 0}我懒洋洋地处理输出格式,使用libfmt而不是再次编写向量打印cruft;Coliru还没有libfmt (或c++23)
https://stackoverflow.com/questions/69480328
复制相似问题