使用 boost-spirit 解析 ipv4 地址

Posted

技术标签:

【中文标题】使用 boost-spirit 解析 ipv4 地址【英文标题】:Parsing ipv4 addresses using boost-spirit 【发布时间】:2012-07-03 09:19:01 【问题描述】:

我想使用 boost::spirit 来解析 ipv4 地址。这是我尝试做的:

#include <string>
#include <string.h>
#include <iostream>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>

struct Ipv4  union  uint32_t as_int; uint8_t as_char[4];  raw; ;

Ipv4 make_ipv4(uint8_t i1, uint8_t i2, uint8_t i3, uint8_t i4) 
    Ipv4 ip;
    ip.raw.as_char[0] = i1; ip.raw.as_char[1] = i2;
    ip.raw.as_char[2] = i3; ip.raw.as_char[3] = i4;
    return ip;


namespace qi = boost::spirit::qi;
qi::uint_parser<uint8_t, 10, 1, 3> octet;

struct Ipv4Address : qi::grammar<const char *, Ipv4()> 
    Ipv4Address() : Ipv4Address::base_type(start) 
        start = ( octet >> qi::lit('.') >> octet >> qi::lit('.') >>
                  octet >> qi::lit('.') >> octet
                ) [
                    //qi::_val = make_ipv4(1, 2, 3, 4) // working
                    qi::_val = make_ipv4(qi::_1, qi::_2, qi::_3, qi::_4) // compile error
                ]
        ;
    
    qi::rule<const char *, Ipv4()> start;
 ipv4_address;

int main() 
    Ipv4 ip;
    const char * s = "1.2.3.4";
    bool r = qi::parse(s, s+strlen(s), ipv4_address, ip);
    std::cout << r << " " << (int)ip.raw.as_char[0] << "." <<
                             (int)ip.raw.as_char[1] << "." <<
                             (int)ip.raw.as_char[2] << "." <<
                             (int)ip.raw.as_char[3] << std::endl;

编译时出现以下编译错误:

/tmp/ip.cxx:在构造函数“Ipv4Address::Ipv4Address()”中: /tmp/ip.cxx:26:72:错误:无法转换'const _1_type aka const boost::phoenix::actor >' 到 'uint8_t aka unsigned char' 用于参数 '1' 到 'Ipv4 make_ipv4(uint8_t, uint8_t, uint8_t, uint8_t)'

有什么提示吗?

这是“正确”的做法吗?

【问题讨论】:

【参考方案1】:

语义动作中需要使用bind懒惰绑定函数:

namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;
qi::uint_parser<uint8_t, 10, 1, 3> octet;

struct Ipv4Address : qi::grammar<const char *, Ipv4()> 
    Ipv4Address() : Ipv4Address::base_type(start) 
        start = ( octet >> qi::lit('.') >> octet >> qi::lit('.') >>
                  octet >> qi::lit('.') >> octet
                ) [
                    qi::_val = phx::bind(make_ipv4, qi::_1, qi::_2, qi::_3, qi::_4)
                ]
        ;
    
    qi::rule<const char *, Ipv4()> start;
 ipv4_address;

完整样本:

#include <string>
#include <string.h>
#include <iostream>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>

struct Ipv4  union  uint32_t as_int; uint8_t as_char[4];  raw; ;

Ipv4 make_ipv4(uint8_t i1, uint8_t i2, uint8_t i3, uint8_t i4) 
    Ipv4 ip;
    ip.raw.as_char[0] = i1; ip.raw.as_char[1] = i2;
    ip.raw.as_char[2] = i3; ip.raw.as_char[3] = i4;
    return ip;


namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;
qi::uint_parser<uint8_t, 10, 1, 3> octet;

struct Ipv4Address : qi::grammar<const char *, Ipv4()> 
    Ipv4Address() : Ipv4Address::base_type(start) 
        start = ( octet >> qi::lit('.') >> octet >> qi::lit('.') >>
                  octet >> qi::lit('.') >> octet
                ) [
                    qi::_val = phx::bind(make_ipv4, qi::_1, qi::_2, qi::_3, qi::_4)
                ]
        ;
    
    qi::rule<const char *, Ipv4()> start;
 ipv4_address;

int main() 
    Ipv4 ip;
    const char * s = "1.2.3.4";
    bool r = qi::parse(s, s+strlen(s), ipv4_address, ip);
    std::cout << r << " " << (int)ip.raw.as_char[0] << "." <<
                             (int)ip.raw.as_char[1] << "." <<
                             (int)ip.raw.as_char[2] << "." <<
                             (int)ip.raw.as_char[3] << std::endl;

【讨论】:

我怎样才能用可选的端口号修改这个优秀的例子。我正在尝试编写一个 CSV 解析器,它接受带有可选端口说明符的 ip 地址,格式为 x.y.z.w:nnnnn,其中 nnnnn 是 1-65535 范围内的小数。 bool r = qi::parse(s, s+strlen(s), ipv4_address &gt;&gt; (":" &gt; qi::uint_parser&lt;uint16_t&gt;() | qi::attr(0u)), ip, port); 会做得很好@johnco3

以上是关于使用 boost-spirit 解析 ipv4 地址的主要内容,如果未能解决你的问题,请参考以下文章

IPv4与IPv6的区别

【Windows Server 2019】DNS服务器的配置与管理——DNS反向解析

IPv4 地址的子网划分与子网掩码 | 含练习题与解析

windows下arp缓存刷新间隔为多久

IPv4

IPAddress.Parse() 使用 IPv4 上的端口