You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 02:06:03