Boost 跳过解析器是正确的方法吗?
Is Boost skip parser the right approach?
经过一些延迟后,我现在再次尝试解析一些 ASCII 文本文件
被一些二进制字符包围。
但是,如果跳过解析器是正确的方法,我现在正在苦苦挣扎?
该文件(它是一个 JEDEC 文件)的语法非常简单:
文件中的每个数据字段都以单个字母开头并以星号结尾。数据字段可以包含空格和回车return。
星号之后的空格和回车 return 也可能跟在
下一个字段标识符。
这就是我用来开始为这样的文件构建解析器的方法:
phrase_parse(first, last,
// First char in File
char_('\x02') >>
// Data field
*((print[cout << _1] | graph[cout << _1]) - char_('*')) >>
// End of data followed by 4 digit hexnumber. How to limit?
char_('\x03') >> *xdigit,
// Skip asterisks
char_('*') );
不幸的是,我没有从这一个得到任何输出。有人知道哪里出了问题吗?
示例文件:
<STX>
JEDEC file generated by John Doe*
DM SIGNETICS(PHILIPS)*
DD GAL16R8*
QP20*
QV0*
G0*F0*
L00000 1110101111100110111101101110111100111111*
CDEAD*
<ETX>BEEF
这就是我想要实现的目标:
Start: JEDEC file generated by John Doe
D: M SIGNETICS(PHILIPS)
D: D GAL16R8
Q: P20
Q: V0
G: 0
F: 0
L: 00000 1110101111100110111101101110111100111111
C: DEAD
End: BEEF
我建议您只想在顶层规则中使用船长。并用它来跳过无关紧要的白色space.
您没有为 星号 使用船长,因为您不想 忽略它们。如果它们被忽略,您的规则将无法对它们起作用。
此外,内部规则不应使用 space 船长,原因很简单,白色 space 和换行在 JEDEC 中是有效的字段数据。
所以,所有这一切的结果是:
value = *(ascii::char_("\x20-\x7e\r\n") - '*') >> '*';
field = ascii::graph >> value;
start = STX >> value >> *field >> ETX >> xmit_checksum;
与各自的船长一起宣布规则的地方:
qi::uint_parser<uint16_t, 16, 4, 4> xmit_checksum;
qi::rule<It, ascii::space_type> start;
qi::rule<It> field, value; // no skippers - they are lexemes
Take-away: Split your grammar up in rules. Be happier for it.
正在处理结果
您的示例不必要地混合了解析和 "printing" 的责任。
我建议不要在这里使用语义操作 (Boost Spirit: "Semantic actions are evil"?)。
相反,声明适当的属性类型:
struct JEDEC {
std::string caption;
struct field {
char id;
std::string value;
};
std::vector<field> fields;
uint16_t checksum;
};
并在您的规则中声明它们:
qi::rule<It, ast::JEDEC(), ascii::space_type> start;
qi::rule<It, ast::JEDEC::field()> field;
qi::rule<It, std::string()> value;
qi::uint_parser<uint16_t, 16, 4, 4> xmit_checksum;
现在,您的语法无需更改,您可以使用以下命令打印所需的输出:
inline static std::ostream& operator<<(std::ostream& os, JEDEC const& jedec) {
os << "Start: " << jedec.caption << "\n";
for(auto& f : jedec.fields)
os << f.id << ": " << f.value << "\n";
auto saved = os.rdstate();
os << "End: " << std::hex << std::setw(4) << std::setfill('0') << jedec.checksum;
os.setstate(saved);
return os;
}
现场演示
这是一个演示程序,它使用您问题中的示例输入将其联系在一起:
//#define BOOST_SPIRIT_DEBUG
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iomanip>
namespace qi = boost::spirit::qi;
namespace ascii = qi::ascii;
namespace ast {
struct JEDEC {
std::string caption;
struct field {
char id;
std::string value;
};
std::vector<field> fields;
uint16_t checksum;
};
inline static std::ostream& operator<<(std::ostream& os, JEDEC const& jedec) {
os << "Start: " << jedec.caption << "\n";
for(auto& f : jedec.fields)
os << f.id << ": " << f.value << "\n";
auto saved = os.rdstate();
os << "End: " << std::hex << std::setw(4) << std::setfill('0') << std::uppercase << jedec.checksum;
os.setstate(saved);
return os;
}
}
BOOST_FUSION_ADAPT_STRUCT(ast::JEDEC::field,
(char, id)(std::string, value))
BOOST_FUSION_ADAPT_STRUCT(ast::JEDEC,
(std::string, caption)
(std::vector<ast::JEDEC::field>, fields)
(uint16_t, checksum))
template <typename It>
struct JedecGrammar : qi::grammar<It, ast::JEDEC(), ascii::space_type>
{
JedecGrammar() : JedecGrammar::base_type(start) {
const char STX = '\x02';
const char ETX = '\x03';
value = *(ascii::char_("\x20-\x7e\r\n") - '*') >> '*';
field = ascii::graph >> value;
start = STX >> value >> *field >> ETX >> xmit_checksum;
BOOST_SPIRIT_DEBUG_NODES((start)(field)(value))
}
private:
qi::rule<It, ast::JEDEC(), ascii::space_type> start;
qi::rule<It, ast::JEDEC::field()> field;
qi::rule<It, std::string()> value;
qi::uint_parser<uint16_t, 16, 4, 4> xmit_checksum;
};
int main() {
typedef boost::spirit::istream_iterator It;
It first(std::cin>>std::noskipws), last;
JedecGrammar<It> g;
ast::JEDEC jedec;
bool ok = phrase_parse(first, last, g, ascii::space, jedec);
if (ok)
{
std::cout << "Parse success\n";
std::cout << jedec;
}
else
std::cout << "Parse failed\n";
if (first != last)
std::cout << "Remaining input unparsed: '" << std::string(first, last) << "'\n";
}
输出:
Start: JEDEC file generated by John Doe
D: M SIGNETICS(PHILIPS)
D: D GAL16R8
Q: P20
Q: V0
G: 0
F: 0
L: 00000 1110101111100110111101101110111100111111
C: DEAD
End: BEEF
Take-away: See your dentist twice a year.
经过一些延迟后,我现在再次尝试解析一些 ASCII 文本文件 被一些二进制字符包围。
但是,如果跳过解析器是正确的方法,我现在正在苦苦挣扎?
该文件(它是一个 JEDEC 文件)的语法非常简单:
文件中的每个数据字段都以单个字母开头并以星号结尾。数据字段可以包含空格和回车return。 星号之后的空格和回车 return 也可能跟在 下一个字段标识符。
这就是我用来开始为这样的文件构建解析器的方法:
phrase_parse(first, last,
// First char in File
char_('\x02') >>
// Data field
*((print[cout << _1] | graph[cout << _1]) - char_('*')) >>
// End of data followed by 4 digit hexnumber. How to limit?
char_('\x03') >> *xdigit,
// Skip asterisks
char_('*') );
不幸的是,我没有从这一个得到任何输出。有人知道哪里出了问题吗?
示例文件:
<STX>
JEDEC file generated by John Doe*
DM SIGNETICS(PHILIPS)*
DD GAL16R8*
QP20*
QV0*
G0*F0*
L00000 1110101111100110111101101110111100111111*
CDEAD*
<ETX>BEEF
这就是我想要实现的目标:
Start: JEDEC file generated by John Doe
D: M SIGNETICS(PHILIPS)
D: D GAL16R8
Q: P20
Q: V0
G: 0
F: 0
L: 00000 1110101111100110111101101110111100111111
C: DEAD
End: BEEF
我建议您只想在顶层规则中使用船长。并用它来跳过无关紧要的白色space.
您没有为 星号 使用船长,因为您不想 忽略它们。如果它们被忽略,您的规则将无法对它们起作用。
此外,内部规则不应使用 space 船长,原因很简单,白色 space 和换行在 JEDEC 中是有效的字段数据。
所以,所有这一切的结果是:
value = *(ascii::char_("\x20-\x7e\r\n") - '*') >> '*';
field = ascii::graph >> value;
start = STX >> value >> *field >> ETX >> xmit_checksum;
与各自的船长一起宣布规则的地方:
qi::uint_parser<uint16_t, 16, 4, 4> xmit_checksum;
qi::rule<It, ascii::space_type> start;
qi::rule<It> field, value; // no skippers - they are lexemes
Take-away: Split your grammar up in rules. Be happier for it.
正在处理结果
您的示例不必要地混合了解析和 "printing" 的责任。 我建议不要在这里使用语义操作 (Boost Spirit: "Semantic actions are evil"?)。
相反,声明适当的属性类型:
struct JEDEC {
std::string caption;
struct field {
char id;
std::string value;
};
std::vector<field> fields;
uint16_t checksum;
};
并在您的规则中声明它们:
qi::rule<It, ast::JEDEC(), ascii::space_type> start;
qi::rule<It, ast::JEDEC::field()> field;
qi::rule<It, std::string()> value;
qi::uint_parser<uint16_t, 16, 4, 4> xmit_checksum;
现在,您的语法无需更改,您可以使用以下命令打印所需的输出:
inline static std::ostream& operator<<(std::ostream& os, JEDEC const& jedec) {
os << "Start: " << jedec.caption << "\n";
for(auto& f : jedec.fields)
os << f.id << ": " << f.value << "\n";
auto saved = os.rdstate();
os << "End: " << std::hex << std::setw(4) << std::setfill('0') << jedec.checksum;
os.setstate(saved);
return os;
}
现场演示
这是一个演示程序,它使用您问题中的示例输入将其联系在一起:
//#define BOOST_SPIRIT_DEBUG
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iomanip>
namespace qi = boost::spirit::qi;
namespace ascii = qi::ascii;
namespace ast {
struct JEDEC {
std::string caption;
struct field {
char id;
std::string value;
};
std::vector<field> fields;
uint16_t checksum;
};
inline static std::ostream& operator<<(std::ostream& os, JEDEC const& jedec) {
os << "Start: " << jedec.caption << "\n";
for(auto& f : jedec.fields)
os << f.id << ": " << f.value << "\n";
auto saved = os.rdstate();
os << "End: " << std::hex << std::setw(4) << std::setfill('0') << std::uppercase << jedec.checksum;
os.setstate(saved);
return os;
}
}
BOOST_FUSION_ADAPT_STRUCT(ast::JEDEC::field,
(char, id)(std::string, value))
BOOST_FUSION_ADAPT_STRUCT(ast::JEDEC,
(std::string, caption)
(std::vector<ast::JEDEC::field>, fields)
(uint16_t, checksum))
template <typename It>
struct JedecGrammar : qi::grammar<It, ast::JEDEC(), ascii::space_type>
{
JedecGrammar() : JedecGrammar::base_type(start) {
const char STX = '\x02';
const char ETX = '\x03';
value = *(ascii::char_("\x20-\x7e\r\n") - '*') >> '*';
field = ascii::graph >> value;
start = STX >> value >> *field >> ETX >> xmit_checksum;
BOOST_SPIRIT_DEBUG_NODES((start)(field)(value))
}
private:
qi::rule<It, ast::JEDEC(), ascii::space_type> start;
qi::rule<It, ast::JEDEC::field()> field;
qi::rule<It, std::string()> value;
qi::uint_parser<uint16_t, 16, 4, 4> xmit_checksum;
};
int main() {
typedef boost::spirit::istream_iterator It;
It first(std::cin>>std::noskipws), last;
JedecGrammar<It> g;
ast::JEDEC jedec;
bool ok = phrase_parse(first, last, g, ascii::space, jedec);
if (ok)
{
std::cout << "Parse success\n";
std::cout << jedec;
}
else
std::cout << "Parse failed\n";
if (first != last)
std::cout << "Remaining input unparsed: '" << std::string(first, last) << "'\n";
}
输出:
Start: JEDEC file generated by John Doe
D: M SIGNETICS(PHILIPS)
D: D GAL16R8
Q: P20
Q: V0
G: 0
F: 0
L: 00000 1110101111100110111101101110111100111111
C: DEAD
End: BEEF
Take-away: See your dentist twice a year.