Boost跳过解析器是正确的方法吗

Is Boost skip parser the right approach?

本文关键字:方法 Boost      更新时间:2023-10-16

经过一段时间的延迟,我现在再次尝试解析一些ASCII文本文件被一些二进制字符包围。

使用boost Spririt 解析带有二进制信封的文本文件

然而,我现在很难确定跳过解析器是否是正确的方法?

该文件的语法(它是一个JEDEC文件)非常简单:

文件中的每个数据字段都以一个字母开头,并以星号结束。数据字段可以包含空格和回车。在星号之后,空格和回车也可能跟在下一个字段标识符。

这就是我开始为这样一个文件构建解析器的原因:

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

我建议您只想在顶层规则中使用队长。并使用它跳过不重要的空白。

星号不使用船长,因为不想忽略它们。如果他们被忽视了,你的规则就不能对他们采取行动。

此外,内部规则不应该使用空格队长,原因很简单,空白和换行符是JEDEC中的有效字段数据。

因此,这一切的结果将是:

value = *(ascii::char_("x20-x7ern") - '*') >> '*';
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

拿走:把你的语法分成规则。为此感到高兴。

处理结果

您的示例不必要地混合了解析和"打印"的职责。我建议不要在这里使用语义动作(Boost Spirit:"语义动作是邪恶的"?)。

相反,声明适当的属性类型:

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;
}

现场演示

这里有一个演示程序,使用您的问题中的样本输入将其联系在一起:

在Coliru上直播

//#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-x7ern") - '*') >> '*';
        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 successn";
        std::cout << jedec;
    }
    else
        std::cout << "Parse failedn";
    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

带走:每年看两次牙医。