Boost.Spirit : 如何解析长度在前的字节数组?
Boost.Spirit : how to parse length preceeding byte array?
我需要解析以下字节数组“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 中使用本地人(具有相同名称的本地人)。
例如,这样的代码有效吗? :
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...
我做错了什么?
-谢谢
要事第一:
grammar.hpp:75:13: error: static assertion failed: incompatible_start_rule...
表示(惊讶)您使用了不兼容的启动规则。违规者是语法基类声明中缺少的 locals<>
参数。与其将实现细节添加到 public 接口,不如考虑使用包装开始规则调用真正的解析器入口点,确实 具有 locals<>
参数。
还有:
m_priority
是什么意思?你的问题没有解决它,样本输入也没有(所以它不应该解析,因为只有 8 字节元素,没有优先级可遵循)。
是不是忘记适配了AVLData
?
忽略这一点,具有语义操作的规则不会自动传播它们的属性。这很好,因为您可能不需要 AST 节点中的那些冗余计数(m_numOfData
和 m_numOfData_last
)
您可以通过使用 operator%=
而不是 operator=
来分配规则定义来强制自动传播。
您可以使用omit
从合成属性中省略属性
您可能想要验证 opening/closing 字节,例如:
uint_byte_p(0x08)
检查结束字节是否匹配第二个说:
qi::omit[uint_byte_p [ qi::_pass = (qi::_a == qi::_1) ] ]
Thanks for @jv_ making be double-check again, you can indeed just say omit(uint_byte_p(_a))
there too.
如果您的语法指定了 ascii::blank_type
,则您不能为它传递 qi::blank
。它需要匹配。再一次:考虑使用开始规则隐藏船长,而不是暴露实现细节。
此外,在这个特定示例中,如果您真的想在输入字符串中的任何地方都接受空格,我会感到惊讶。另请注意,int_parser
隐含地 lexeme(这意味着即使在此配置中,数组元素或字节也不能包含空格)。你应该看看这是否符合你的要求。
你对期望点的使用实际上排除了解析失败而无一例外的可能性(除非第一个字节无法解析,因为第一个 uint_byte_p
前面没有期望点如 qi::eps > uint_byte_p
)。考虑使用 >>
来获得正常的序列语义。
解决这些问题会产生工作代码:
//#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>
奖金:
Can I use the local across rules?
没有。您需要继承属性:
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;
我需要解析以下字节数组“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 中使用本地人(具有相同名称的本地人)。 例如,这样的代码有效吗? :
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...
我做错了什么?
-谢谢
要事第一:
grammar.hpp:75:13: error: static assertion failed: incompatible_start_rule...
表示(惊讶)您使用了不兼容的启动规则。违规者是语法基类声明中缺少的 locals<>
参数。与其将实现细节添加到 public 接口,不如考虑使用包装开始规则调用真正的解析器入口点,确实 具有 locals<>
参数。
还有:
m_priority
是什么意思?你的问题没有解决它,样本输入也没有(所以它不应该解析,因为只有 8 字节元素,没有优先级可遵循)。是不是忘记适配了
AVLData
?忽略这一点,具有语义操作的规则不会自动传播它们的属性。这很好,因为您可能不需要 AST 节点中的那些冗余计数(
m_numOfData
和m_numOfData_last
)您可以通过使用
operator%=
而不是operator=
来分配规则定义来强制自动传播。您可以使用
omit
从合成属性中省略属性您可能想要验证 opening/closing 字节,例如:
uint_byte_p(0x08)
检查结束字节是否匹配第二个说:qi::omit[uint_byte_p [ qi::_pass = (qi::_a == qi::_1) ] ]
Thanks for @jv_ making be double-check again, you can indeed just say
omit(uint_byte_p(_a))
there too.如果您的语法指定了
ascii::blank_type
,则您不能为它传递qi::blank
。它需要匹配。再一次:考虑使用开始规则隐藏船长,而不是暴露实现细节。此外,在这个特定示例中,如果您真的想在输入字符串中的任何地方都接受空格,我会感到惊讶。另请注意,
int_parser
隐含地 lexeme(这意味着即使在此配置中,数组元素或字节也不能包含空格)。你应该看看这是否符合你的要求。你对期望点的使用实际上排除了解析失败而无一例外的可能性(除非第一个字节无法解析,因为第一个
uint_byte_p
前面没有期望点如qi::eps > uint_byte_p
)。考虑使用>>
来获得正常的序列语义。
解决这些问题会产生工作代码:
//#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>
奖金:
Can I use the local across rules?
没有。您需要继承属性:
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;