gpt4 book ai didi

c++ - 使用 boost::spirit::qi 解析带分隔符的数字

转载 作者:太空狗 更新时间:2023-10-29 20:12:08 25 4
gpt4 key购买 nike

我正在尝试使用 boost::spirit::qi 进行一些解析。它实际上进展顺利,我成功地根据后缀成功地解析了各种基数中的数字。示例:123、c12h、777o、110101b。

然后我想添加允许完全忽略分隔符的功能,以允许解析 123_456 或 1101_0011b 等值。我尝试使用跳过解析器,但我高度怀疑我完全误解了它的使用方式。它编译得很好,但我试图让它忽略下划线根本没有任何作用。任何有关如何使它做我想做的事情的建议都将不胜感激。我的测试代码如下:

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>

namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
using qi::_val;
using qi::_1;
using qi::skip;
using qi::uint_parser;
using ascii::char_;

template <typename Iterator>
struct unsigned_parser : qi::grammar<Iterator, uint64_t()> {

unsigned_parser() : unsigned_parser::base_type(start) {
uint_parser<uint64_t, 10> dec_parser;
uint_parser<uint64_t, 16> hex_parser;
uint_parser<uint64_t, 8> oct_parser;
uint_parser<uint64_t, 2> bin_parser;

start = skip(char_('_'))[
/* binary with suffix */
(bin_parser[_val=_1] >> char_("bByY"))
/* octal with suffix */
| (oct_parser[_val=_1] >> char_("qQoO"))
/* hexadecimal with suffix */
| (hex_parser[_val=_1] >> char_("hHxX"))
/* decimal with optional suffix */
| (dec_parser[_val=_1] >> -char_("dDtT"))
];
}

qi::rule<Iterator, uint64_t()> start;
};

int main(int argv, const char *argc[]) {
typedef std::string::const_iterator iter;
unsigned_parser<iter> up;
uint64_t val;
if (argv != 2) {
std::cerr << "Usage: " << argc[0] << " <input>" << std::endl;
return 1;
}
std::string test(argc[1]);
iter i = test.begin();
iter end = test.end();
bool rv = parse(i, end, up, val);
if (rv && i == end) {
std::cout << "Succeeded: " << val << std::endl;
return 0;
}
if (rv) {
std::cout << "Failed partial parse: " << val << std::endl;
return 1;
}
std::cout << "Failed." << std::endl;
return 1;
}

最佳答案

噢。没有人应该为 Spirit 解析器上下文等实现细节操心,除非您要扩展库并实现自己的解析器指令

在那之前,phoenix::function<> , phoenix::bind甚至 BOOST_PHOENIX_ADAPT_FUNCTION对任何人来说都应该足够了。

这里有两种方法可以解决您的问题,无需对库进行任何修补。

  1. 直接解析 Live On Coliru

    这可以被视为仅使用 Qi 和简单的语义操作来解析不同样式的整数的“天真”方式:

    start = 
    eps [_val=0] >> +(char_("0-9a-fA-F") [ _val = _val*16 + _decode(_1) ] | '_')>> char_("hHxX") /* hexadecimal with suffix */
    | eps [_val=0] >> +(char_("0-7") [ _val = _val* 8 + _decode(_1) ] | '_')>> char_("qQoO") /* octal with suffix */
    | eps [_val=0] >> +(char_("01") [ _val = _val* 2 + _decode(_1) ] | '_')>> char_("bByY") /* binary with suffix */
    | eps [_val=0] >> +(char_("0-9") [ _val = _val*10 + _decode(_1) ] | '_')>> -char_("dDtT") /* decimal with optional suffix */
    ;

    当然,你会想知道什么_decode好像。那么你自己定义它:

    struct decode {
    template <typename> struct result { typedef int type; };
    template <typename Ch> int operator()(Ch ch) const {
    if (ch>='0' && ch<='9') return ch - '0';
    if (ch>='a' && ch<='z') return ch - 'a' + 10;
    if (ch>='A' && ch<='Z') return ch - 'A' + 10;
    assert(false);
    }
    };
    boost::phoenix::function<decode> _decode;
  2. 使用 BOOST_PHOENIX_ADAPT_FUNCTION Live On Coliru

    您可以使用宏来代替定义函数对象

    int decode(char ch) {
    if (ch>='0' && ch<='9') return ch - '0';
    if (ch>='a' && ch<='z') return ch - 'a' + 10;
    if (ch>='A' && ch<='Z') return ch - 'A' + 10;
    assert(false);
    }

    BOOST_PHOENIX_ADAPT_FUNCTION(int, _decode, decode, 1)
  3. 使用 std::strtoul Live On Coliru

    当然,上面的内容可能有点“复杂”,因为它需要您处理整数运算和数字解码的具体细节。

    另外,如果字面值是一个十进制值,如“101_101”,“天真”方法会做一些重复的工作。它将计算十六进制、八进制 二进制分支的子结果,然后才意识到它是十进制。

    所以我们可以改变顺序:

    start = 
    (raw[+char_("_0-9a-fA-F")] >> char_("hHxX")) [ _val = _strtoul(_1,16) ] /* hexadecimal with suffix */
    | (raw[+char_("_0-7")] >> char_("qQoO")) [ _val = _strtoul(_1, 8) ] /* octal with suffix */
    | (raw[+char_("_01")] >> char_("bByY")) [ _val = _strtoul(_1, 2) ] /* binary with suffix */
    | (raw[+char_("_0-9")] >> -char_("dDtT")) [ _val = _strtoul(_1,10) ] /* decimal with optional suffix */
    ;

    您会再次好奇我们是如何实现 _evaluate 的?这是一个从 raw 中获取合成属性的函数(这是一个迭代器范围)和基数,这在那时肯定是已知的:

    struct strtoul_f {
    template <typename, typename> struct result { typedef uint64_t type; };
    template <typename Raw, typename Int> uint64_t operator()(Raw raw, Int base) const {
    std::string s(raw.begin(), raw.end());
    s.erase(std::remove(s.begin(), s.end(), '_'), s.end());
    char *f(&s[0]), *l(f+s.size());
    return std::strtoul(f, &l, base);
    }
    };
    boost::phoenix::function<strtoul_f> _strtoul;

    如您所见,唯一复杂的是删除 _从范围开始。

关于c++ - 使用 boost::spirit::qi 解析带分隔符的数字,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29132809/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com