Boost.Spirit:如何解析字节数组之前的长度?

时间:2016-05-07 20:56:46

标签: c++ boost binary hex boost-spirit

我需要解析以下字节数组" 080100000113fc208dff01"。

这里:

  • 第1个字节" 08" - ID
  • 第2个字节" 01" - 8字节数组的长度
  • 3-10个字节 - 8字节数组的元素
  • 第11个字节" 01" - 8字节数组的长度(应与第2字节相同)

我试图使用qi :: repeat(),按照手册并实现了以下解析器Link To Coliru

#define BOOST_SPIRIT_DEBUG
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
#include <vector>

namespace qi = boost::spirit::qi;

typedef unsigned int BYTE;
typedef unsigned long long ULONGLONG;

struct AVLData
{
    ULONGLONG m_timestamp;
    BYTE m_priority;
};

struct AVLDataArray
{
    BYTE m_codecID;
    BYTE m_numOfData;
    std::vector<AVLData> m_data;
    BYTE m_numOfData_last;
};

BOOST_FUSION_ADAPT_STRUCT(AVLDataArray, m_codecID, m_numOfData, m_data,     m_numOfData_last)

template <typename Iterator, typename Skipper = qi::ascii::blank_type>
    struct Grammar: qi::grammar <Iterator, AVLDataArray(), Skipper>
    {
        Grammar() : Grammar::base_type(avl_array)
        {
            qi::uint_parser<BYTE, 16, 2, 2> uint_byte_p;
            qi::uint_parser<unsigned long long, 16, 16, 16> uint_8_byte_p;

            avl_array = uint_byte_p > uint_byte_p[qi::_a = qi::_1] >    qi::repeat(qi::_a)[uint_8_byte_p > uint_byte_p] > uint_byte_p;

            BOOST_SPIRIT_DEBUG_NODES((avl_array));
        }

    private:
        qi::rule<Iterator, AVLDataArray(), Skipper, qi::locals<BYTE>> avl_array;
};

int main() {
    std::string const input = "080100000113fc208dff01";

    auto f(begin(input)), l(end(input));
    Grammar<std::string::const_iterator> g;

    AVLDataArray array;
    bool ok = qi::phrase_parse(f,l,g,qi::blank,array);

    if (ok && f == l) 
    {
        std::cout << "Parse succeeded\n";
    } else
    {
        std::cout << "Parse failed\n";
         std::cout << "->stopped at [" + std::string(f, l) + "]";
    }

    return 0;
}

但就目前而言,我遇到了两个问题:

1)我不确定我是否理解如何在2 qi :: rules中使用locals(同名的本地)。 例如,这样的代码是否有效? :

data = qi::repeat(qi::_a)[uint_8_byte_p > uint_byte_p];
vl_array = uint_byte_p > uint_byte_p[qi::_a = qi::_1] > data > uint_byte_p;

2)我的例子没有编译错误

grammar.hpp:75:13: error: static assertion failed: incompatible_start_rule...

我做错了什么?

-Thanks

1 个答案:

答案 0 :(得分:4)

首先要做的事情:

  

grammar.hpp:75:13:错误:静态断言失败:incompatible_start_rule ...

表示(惊讶)您使用不兼容的启动规则。违规者是语法基类声明中缺少的locals<>参数。不要将该实现细节添加到公共接口,而应考虑使用包装启动规则来调用 具有locals<>参数的实际解析器入口点。

更多:

  • m_priority的内容是什么?你的问题没有得到解决,样本输入也没有(所以它不应该解析,因为只有8字节的元素,没有优先顺序)。

  • 您是否忘了调整AVLData

  • 忽略这一点,具有语义动作的规则不会自动传播其属性。这很好,因为您可能不需要AST节点中的那些冗余计数(m_numOfDatam_numOfData_last

    您可以使用operator%=代替operator=来强制自动传播,以分配规则定义。

  • 您可以使用omit省略合成属性中的属性

  • 您可能想要验证打开/关闭字节,例如:

    uint_byte_p(0x08)
    

    检查结束字节是否与第二个匹配:

    qi::omit[uint_byte_p [ qi::_pass = (qi::_a == qi::_1) ] ]
    

    <击>

      

    感谢@jv_再次仔细检查,你也可以在那里说omit(uint_byte_p(_a))

  • 如果您的语法指定ascii::blank_type,则无法通过qi::blank。{li>

    它需要匹配。再次:考虑使用启动规则隐藏船长,而不是暴露实施细节。

  • 此外,在这个特定示例中,如果您真的想在输入字符串中的任何地方接受空白,我会感到惊讶。另请注意,int_parser隐式 lexeme (意味着即使在此配置中,数组元素或字节也不能包含空格)。您应该检查这一切是否符合您的要求。

  • 您对期望点的使用实际上排除了解析在没有异常的情况下失败的可能性(除非第一个字节无法解析,因为第一个uint_byte_p之前没有预期点比如qi::eps > uint_byte_p)。考虑使用>>来获得正常的序列语义。

解决这些问题导致工作代码:

<强> Live On Coliru

//#define BOOST_SPIRIT_DEBUG
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
#include <vector>
#include <iomanip>

namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;

typedef unsigned int BYTE; // what large bytes you have, grandma!?

struct AVLData {
    uint64_t m_timestamp;
    BYTE m_priority;
};

struct AVLDataArray {
    BYTE m_codecID;
    std::vector<AVLData> m_data;
};

BOOST_FUSION_ADAPT_STRUCT(AVLData, m_timestamp, m_priority) // you need to adapt all your types
BOOST_FUSION_ADAPT_STRUCT(AVLDataArray, m_codecID, m_data)

template <typename Iterator, typename Skipper = ascii::blank_type>
    struct Grammar: qi::grammar <Iterator, AVLDataArray(), Skipper>
    {
        Grammar() : Grammar::base_type(start)
        {
            qi::uint_parser<BYTE, 16, 2, 2> uint_byte_p;
            qi::uint_parser<uint64_t, 16, 16, 16> uint_8_byte_p;

            avl_array %= uint_byte_p(0x08)
                      >> qi::omit[uint_byte_p[qi::_a = qi::_1]] 
                      >> qi::repeat(qi::_a)[uint_8_byte_p >> uint_byte_p]
                      >> qi::omit[uint_byte_p [ qi::_pass = (qi::_a == qi::_1) ] ]
                      ;

            start      = avl_array;

            BOOST_SPIRIT_DEBUG_NODES((avl_array)(start));
        }

    private:
        qi::rule<Iterator, AVLDataArray(), Skipper> start;
        qi::rule<Iterator, AVLDataArray(), Skipper, qi::locals<BYTE>> avl_array;
};

int main() {
    std::string const input = "080100000113fc208dff" /*priority:*/ "2a" /*end prioirity*/ "01";

    auto f(begin(input)), l(end(input));
    Grammar<std::string::const_iterator> g;

    AVLDataArray array;
    bool ok = qi::phrase_parse(f,l,g,ascii::blank,array);

    if (ok && f == l) 
    {
        std::cout << "Parse succeeded\n";
        std::cout << "Codec: " << array.m_codecID << "\n";
        for(auto& element : array.m_data)
            std::cout << "element: 0x" << std::hex << element.m_timestamp << " prio " << std::dec << element.m_priority << "\n";
    } else
    {
        std::cout << "Parse failed\n";
        std::cout << "->stopped at [" + std::string(f, l) + "]";
    }

    return 0;
}

打印:

Parse succeeded
Codec: 8
element: 0x113fc208dff prio 42

启用调试信息:

<start>
  <try>080100000113fc208dff</try>
  <avl_array>
    <try>080100000113fc208dff</try>
    <success></success>
    <attributes>[[8, [[1185345998335, 42]]]]</attributes><locals>(1)</locals>
  </avl_array>
  <success></success>
  <attributes>[[8, [[1185345998335, 42]]]]</attributes>
</start>

奖金:

  

我可以使用本地跨规则吗?

没有。您需要继承属性:

<强> Live On Coliru

    data       = qi::repeat(qi::_r1)[uint_8_byte_p >> uint_byte_p]
              ;
    avl_array %= uint_byte_p(0x08)
              >> qi::omit[uint_byte_p[qi::_a = qi::_1]] 
              >> data(qi::_a)
              >> qi::omit[uint_byte_p [ qi::_pass = (qi::_a == qi::_1) ] ]
              ;

规则如下:

qi::rule<Iterator, std::vector<AVLData>(BYTE), Skipper> data;
qi::rule<Iterator, AVLDataArray(),             Skipper, qi::locals<BYTE>> avl_array;