使用Boost Spirit解析Fortran风格固定宽度实数的问题求助
基于Boost Spirit实现固定宽度Fortran D格式浮点数解析
核心问题说明
原生解析规则存在三个明显缺陷:
- 原生
qi::double_仅支持E/e作为指数分隔符,不识别Fortran场景常用的D/d指数标记 - 原生浮点数解析器无宽度限制,会误吞后续固定宽度整数字段的内容,导致后续字段解析失败
- 未实现全空格字段默认返回0值的逻辑
解决方案
第一步:自定义Fortran浮点数解析策略
继承qi::real_policies重载指数解析逻辑,新增对D/d指数分隔符的支持:
template <typename T> struct fortran_real_policy : qi::real_policies<T> { template <typename It> static bool parse_exponent(It& first, It const& last) { if (first == last) return false; // 同时支持E/e/D/d四种指数分隔符 if (*first == 'e' || *first == 'E' || *first == 'd' || *first == 'D') { ++first; return qi::real_policies<T>::parse_exp(first, last); } return false; } }; // 声明适配Fortran格式的浮点数解析器 qi::real_parser<double, fortran_real_policy<double>> fortran_double;
第二步:实现固定宽度解析逻辑
先固定截取19位长度的子串,再对该子串做判断和解析,避免越界读取后续字段:
qi::rule<It, double()> X19 = // 固定截取19个字符 qi::raw[qi::repeat(19)[qi::char_]] [ qi::_val = boost::phoenix::bind([](const std::string& s) -> double { // 全空格返回0 if (s.find_first_not_of(' ') == std::string::npos) return 0.0; double res = 0.0; auto it = s.begin(), end = s.end(); // 用自定义Fortran浮点数解析器解析子串 if (fortran_double.parse(it, end, res)) return res; return 0.0; }, qi::_1) ];
完整可运行代码
#include <boost/spirit/include/qi.hpp> #include <boost/spirit/include/phoenix.hpp> #include <boost/fusion/adapted.hpp> #include <iomanip> #include <string> namespace qi = boost::spirit::qi; template <typename T> struct fortran_real_policy : qi::real_policies<T> { template <typename It> static bool parse_exponent(It& first, It const& last) { if (first == last) return false; if (*first == 'e' || *first == 'E' || *first == 'd' || *first == 'D') { ++first; return qi::real_policies<T>::parse_exp(first, last); } return false; } }; qi::real_parser<double, fortran_real_policy<double>> fortran_double; struct RECORD { uint16_t a{}; double b{}; uint16_t c{}; }; BOOST_FUSION_ADAPT_STRUCT(RECORD, a,b,c) int main() { using It = std::string::const_iterator; using namespace qi::labels; qi::uint_parser<uint16_t, 10, 4, 4> i4; qi::rule<It, double()> X19 = qi::raw[qi::repeat(19)[qi::char_]] [ qi::_val = boost::phoenix::bind([](const std::string& s) -> double { if (s.find_first_not_of(' ') == std::string::npos) return 0.0; double res = 0.0; auto it = s.begin(), end = s.end(); if (fortran_double.parse(it, end, res)) return res; return 0.0; }, qi::_1) ]; for (std::string const str : { "1234 0.000000000000D+001234", "1234 7.654321000000D+001234", "1234 1234", "1234-7.654321000000D+001234", }) { It f = str.cbegin(), l = str.cend(); RECORD rec; if (qi::parse(f, l, (i4 >> X19 >> i4), rec)) { std::cout << "{a:" << rec.a << ", b:" << rec.b << ", c:" << rec.c << "}\n"; } else { std::cout << "Parse fail (" << std::quoted(str) << ")\n"; } } }
运行结果
{a:1234, b:0, c:1234} {a:1234, b:7.65432, c:1234} {a:1234, b:0, c:1234} {a:1234, b:-7.65432, c:1234}
内容的提问来源于stack exchange,提问作者Anton
相关产品推荐
相关产品推荐

