关于Boost Spirit Qi解析PHP代码过滤冗余元素的技术问询
关于Boost Spirit Qi实现PHP代码清理的疑问解答
1. 是否使用了Boost语法解析的最新技术?
你当前使用的Boost Spirit Qi是Spirit框架的第二代解析组件,目前仍在Boost库中维护,但Boost社区在2016年(Boost 1.61版本)引入了更现代的Spirit X3——基于C++14的轻量解析框架,相比Qi有以下优势:
- 编译速度更快(模板实例化更少)
- 内存占用更低
- 语法更简洁,无需显式定义rule的模板参数
- 支持更灵活的错误处理
如果是新学习Boost解析器,推荐优先上手Spirit X3;若需兼容旧代码,Qi仍然是稳定可用的选择。
2. 基于Spirit的实现方式是否正确?
当前实现存在逻辑缺陷,无法正确达成你的需求,主要问题如下:
核心逻辑错误
你调用phrase_parse(begin, end, skipper, skipper)时,将跳过规则(skipper)作为目标解析器,这意味着phrase_parse只会尝试匹配需要移除的内容(注释、namespace等),不会处理需保留的代码。后续仅收集begin到第一个换行的内容,会丢失大部分目标代码,无法得到期望的输出。
规则定义的问题
- 错误使用
standard_wide::char_:你的输入是std::string(窄字符),应使用qi::standard::char_(或在using namespace qi下直接写char_),否则会导致字符匹配异常。 identifier变量未使用,属于冗余代码。- PHP规则有局限性:比如
php_namespace仅匹配到换行或分号,无法处理带花括号的命名空间(如namespace foo { ... }),不过如果你的场景是固定的自动生成模板,可能暂时够用。
正确的实现思路
应定义解析器匹配需要保留的内容,同时用skipper跳过不需要的部分,将保留内容收集起来。示例改进代码:
#include <boost/spirit/include/qi.hpp> #include <string> namespace qi = boost::spirit::qi; std::string non_comments_php_code(const std::string &contents) { std::string result; using Iterator = std::string::const_iterator; Iterator begin = contents.cbegin(), end = contents.cend(); // 定义跳过规则 qi::rule<Iterator> skipper; { using namespace qi; rule<Iterator> single_line_comment = "//" >> *(char_ - eol) >> (eol | eoi); rule<Iterator> block_comment = "/*" >> *(block_comment | (char_ - "*/")) >> "*/"; rule<Iterator> php_tag = lit("<?php") | lit("?>"); rule<Iterator> php_comment = '#' >> *(char_ - eol) >> (eol | eoi); rule<Iterator> php_namespace = lit("namespace ") >> *(char_ - (eol | ';')) >> (eol | ';'); rule<Iterator> php_use = lit("use ") >> *(char_ - (eol | ';')) >> (eol | ';'); skipper = space | single_line_comment | block_comment | php_tag | php_namespace | php_use | php_comment; } // 定义保留内容的解析器:匹配所有非跳过的字符,收集到result中 qi::rule<Iterator, std::string()> keep = +(qi::char_ - skipper); // 解析并收集保留内容 bool ok = qi::phrase_parse(begin, end, *keep, skipper, result); // 追加未解析的剩余内容(理论上不会出现,除非skipper覆盖不全) if (begin != end) { result.append(begin, end); } return result; }
这个版本会正确收集所有不需要跳过的内容,符合你的期望输出。
3. 为何要将语法规则定义放在代码块内?
这个代码块的作用是限制using namespace qi;的作用域:
- 在代码块内使用
using namespace qi;可以让规则定义更简洁(不用每次写qi::前缀)。 - 避免污染外部命名空间,防止
qi中的符号(如char_、eol)与其他代码冲突。
这是C++中常见的命名空间使用技巧,既提升代码可读性,又避免命名冲突。
补充优化:如果你的核心需求只是判断是否存在自定义代码,无需收集完整内容,可进一步简化实现以提升效率:
bool has_custom_php_code(const std::string &contents) { using Iterator = std::string::const_iterator; Iterator begin = contents.cbegin(), end = contents.cend(); qi::rule<Iterator> skipper; { using namespace qi; // 跳过规则定义同上述代码 rule<Iterator> single_line_comment = "//" >> *(char_ - eol) >> (eol | eoi); rule<Iterator> block_comment = "/*" >> *(block_comment | (char_ - "*/")) >> "*/"; rule<Iterator> php_tag = lit("<?php") | lit("?>"); rule<Iterator> php_comment = '#' >> *(char_ - eol) >> (eol | eoi); rule<Iterator> php_namespace = lit("namespace ") >> *(char_ - (eol | ';')) >> (eol | ';'); rule<Iterator> php_use = lit("use ") >> *(char_ - (eol | ';')) >> (eol | ';'); skipper = space | single_line_comment | block_comment | php_tag | php_namespace | php_use | php_comment; } // 尝试跳过所有可跳过的内容,若最后begin != end则说明存在自定义代码 qi::phrase_parse(begin, end, qi::skip(skipper)[qi::eps], skipper); return begin != end; }
内容的提问来源于stack exchange,提问作者Santilín
相关产品推荐
相关产品推荐

