如何用boost::spirit编写兼容IPv4与IPv6的URL解析器?
解决Boost.Spirit URL解析器兼容IPv4/IPv6的问题
核心问题在于冒号同时作为IPv6地址分隔符和IPv4端口分隔符带来的歧义。原代码的host规则贪婪匹配所有包含冒号的字符,导致IPv4的端口被误纳入host部分。我们可以通过拆分host规则,区分不同类型的主机地址来解决这个问题。
修正后的代码
#include <boost/spirit/include/qi.hpp> #include <boost/fusion/include/adapt_struct.hpp> #include <string> struct UrlParts { std::string scheme; std::string host; std::string port; std::string path; }; BOOST_FUSION_ADAPT_STRUCT( UrlParts, (std::string, scheme) (std::string, host) (std::string, port) (std::string, path) ) void parseUrl_BoostSpirit(const std::string &input, std::string &prefix, std::string &suffix, std::string &host) { namespace qi = boost::spirit::qi; // 定义各部分规则 qi::rule<std::string::const_iterator, std::string()> scheme_part = -(+qi::char_("a-zA-Z0-9+-.") >> "://"); // 带括号的IPv6主机:捕获括号内的内容 qi::rule<std::string::const_iterator, std::string()> bracketed_ipv6_host = qi::omit['['] >> +qi::char_("a-fA-F0-9:.") >> qi::omit[']']; // 不带括号的IPv6主机:匹配包含冒号的序列,直到遇到斜杠或结束 qi::rule<std::string::const_iterator, std::string()> unbracketed_ipv6_host = +qi::char_("a-fA-F0-9:.") >> &(qi::lit('/') | qi::eoi); // IPv4主机:匹配数字和点,遇到冒号加数字(端口)则停止 qi::rule<std::string::const_iterator, std::string()> ipv4_host = +qi::char_("0-9.") >> !(qi::lit(':') >> +qi::digit) | +qi::char_("0-9."); // 主机规则:优先匹配带括号的IPv6,再是不带括号的IPv6,最后是IPv4 qi::rule<std::string::const_iterator, std::string()> host_part = bracketed_ipv6_host | unbracketed_ipv6_host | ipv4_host; qi::rule<std::string::const_iterator, std::string()> port_part = -(qi::lit(':') >> +qi::digit); qi::rule<std::string::const_iterator, std::string()> path_part = *qi::char_; // 主URL规则 qi::rule<std::string::const_iterator, UrlParts()> url = scheme_part >> host_part >> port_part >> path_part; // 解析输入 UrlParts parts; auto iter = input.begin(); if (qi::parse(iter, input.end(), url, parts)) { prefix = parts.scheme.empty() ? "" : parts.scheme + "://"; host = parts.host; suffix = (parts.port.empty() ? "" : ":" + parts.port) + parts.path; } else { host = input; } }
关键改进说明
- 拆分主机规则:将host分为三个子规则,分别处理带括号的IPv6、不带括号的IPv6和IPv4,通过优先级(带括号IPv6优先)避免歧义。
- 带括号IPv6处理:使用
qi::omit忽略括号,仅捕获括号内的地址作为host。 - 不带括号IPv6处理:通过前瞻断言
&(qi::lit('/') | qi::eoi)确保只匹配到斜杠或字符串结束,避免将后续的端口冒号误判为IPv6的一部分。 - IPv4处理:使用否定前瞻
!(qi::lit(':') >> +qi::digit)确保遇到端口分隔符时停止匹配host,正确区分主机地址和端口。
这样修改后,即可同时正确处理你给出的三种URL格式,无需依赖if-else逻辑,完全利用Boost.Spirit的语法解析能力实现优雅的解决方案。
内容的提问来源于stack exchange,提问作者Aditya Sharma
相关产品推荐
相关产品推荐

