使用boost :: spirit :: *将128位字符串转换为数组

时间:2015-01-21 17:54:11

标签: c++ boost-spirit boost-spirit-qi

我目前正在使用boost :: spirit :: *。我尝试将128位字符串解析为具有相应大小的简单c数组。我创建了一个简短的测试来完成这项工作:

    boost::spirit::qi::int_parser< boost::uint8_t, 16, 2, 2 > uint8_hex;
    std::string src( "00112233445566778899aabbccddeeff" );
    boost::uint8_t dst[ 16 ];

    bool r;
    for( std::size_t i = 0; i < 16; ++i )
    {
        r = boost::spirit::qi::parse( src.begin( ) + 2 * i, src.begin( ) + 2 * i + 2, uint8_hex, dst[ i ] );
    }

我觉得这不是最聪明的方法:)任何想法如何定义规则以便我可以避免循环?

更新

与此同时,我想出了以下能够很好地完成工作的代码:

    using namespace boost::spirit;
    using namespace boost::phoenix;

    qi::int_parser< boost::uint8_t, 16, 2, 2 > uint8_hex;

    std::string src( "00112233445566778899aabbccddeeff" );

    boost::uint8_t dst[ 16 ];
    std::size_t i = 0;

    bool r = qi::parse( src.begin( ),
                        src.end( ),
                        qi::repeat( 16 )[ uint8_hex[ ref( dst )[ ref( i )++ ] = qi::_1 ] ] );

2 个答案:

答案 0 :(得分:2)

如果您真的只想解析128位整数的十六进制表示,那么您可以通过使用Boost Multiprecision中定义的uint128_t来解决这个问题:

qi::int_parser<uint128_t, 16, 16, 16> uint128_hex;

uint128_t parsed;
bool r = qi::parse(f, l, uint128_hex, parsed);

这一定是最快捷的方式,尤其是在指令集支持128位类型的平台上。

<强> Live On Coliru

#include <boost/multiprecision/cpp_int.hpp>
#include <boost/spirit/include/qi.hpp>

namespace qi  = boost::spirit::qi;

int main() {
    using boost::multiprecision::uint128_t;
    using It = std::string::const_iterator;
    qi::int_parser<uint128_t, 16, 16, 16> uint128_hex;

    std::string const src("00112233445566778899aabbccddeeff");
    auto f(src.begin()), l(src.end());

    uint128_t parsed;
    bool r = qi::parse(f, l, uint128_hex, parsed);

    if (r) std::cout << "Parse succeeded: " << std::hex << std::showbase << parsed << "\n";
    else   std::cout << "Parse failed at '" << std::string(f,l) << "'\n";

}

答案 1 :(得分:0)

有一些令人悲伤的因素导致这是一个痛苦的边缘案例

  • Boost Fusion可以调整(boost::)array<>,但它需要解析器才能生成元素元组,而不是容器
  • Boost Fusion可以调整这些序列,但需要配置为允许16个元素:

    #define FUSION_MAX_VECTOR_SIZE 16
    
  • 即使你这样做,qi::repeat(n)[]解析器指令也要求该属性为容器类型。

你可能会以丑陋的方式解决所有这些问题(例如Live On Coliru)。这使得一切都很难在未来发挥作用。

我更喜欢这里的一个微小的语义动作,以便从qi::repeat(n)[]分配结果:

    using data_t = boost::array<uint8_t, 16>;
    data_t dst {};

        qi::rule<It, data_t(), qi::locals<data_t::iterator> > rule = 
            qi::eps [ qi::_a = phx::begin(qi::_val) ]
            >> qi::repeat(16) [
                    uint8_hex [ *qi::_a++ = qi::_1 ]
            ];

这没有太大的噪音。我们的想法是使用start iterator并写入每个iteraton的下一个元素。

<强> Live On Coliru

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

namespace qi  = boost::spirit::qi;
namespace phx = boost::phoenix;

int main() {
    using It = std::string::const_iterator;
    qi::int_parser<uint8_t, 16, 2, 2> uint8_hex;

    std::string const src("00112233445566778899aabbccddeeff");
    auto f(src.begin()), l(src.end());

    using data_t = boost::array<uint8_t, 16>;
    data_t dst {};

        qi::rule<It, data_t(), qi::locals<data_t::iterator> > rule = 
            qi::eps [ qi::_a = phx::begin(qi::_val) ]
            >> qi::repeat(16) [
                    uint8_hex [ *qi::_a++ = qi::_1 ]
            ];

    bool r = qi::parse(f, l, rule, dst);

    if (r) {
        std::cout << "Parse succeeded\n";

        for(unsigned i : dst) std::cout << std::hex << std::showbase << i << " ";
        std::cout << "\n";
    } else {
        std::cout << "Parse failed at '" << std::string(f,l) << "'\n";
    }
}