c++ - 使用 boost spirit 解析带有可选分隔符的字符串

标签 c++ boost boost-spirit boost-spirit-qi

我正在尝试使用特殊规则解析 URL 查询字符串。到目前为止,它适用于下面描述的一个排除项 使用以下方法将 URL 解析为一组键值对:

const qi::rule<std::string::const_iterator, std::string()> key = qi::char_("a-zA-Z_") >> *qi::char_("a-zA-Z_0-9/%\\-_~\\.");
const qi::rule<std::string::const_iterator, std::string()> value = *(qi::char_ - '=' - '&');
const qi::rule<std::string::const_iterator, std::pair<std::string, std::string>()> pair  =  key >> -('=' >> value);
const qi::rule<std::string::const_iterator, std::unordered_map<std::string, std::string>()> query =  pair >> *(('&') >> pair);

到目前为止,还不错。其中一种特殊情况是 & 符号可以以 XML 实体的形式呈现 - & 因此查询规则升级为

const qi::rule<std::string::const_iterator, std::unordered_map<std::string, std::string>()> query =  pair >> *((qi::lit("&amp;")|'&') >> pair);

它按预期工作。然后出现了其他特殊情况 - 引用的值可以包含未转义的等号和&符号,形式为 a=b&d=e&f=$$g=h&i=j$$&x=y&z=def 应该解析成

  • a => b
  • d => e
  • f => g=h&i=j
  • x => y
  • x => def

所以我为“引用”值添加了额外的规则

const qi::rule<std::string::const_iterator, std::string()> key   =  qi::char_("a-zA-Z_") >> *qi::char_("a-zA-Z_0-9/%\\-_~\\.");
const qi::rule<std::string::const_iterator, std::string()> escapedValue = qi::omit["$$"] >> *(qi::char_ - '$') >> qi::omit["$$"];
const qi::rule<std::string::const_iterator, std::string()> value = *(escapedValue | (qi::char_ - '=' - '&'));
const qi::rule<std::string::const_iterator, std::pair<std::string, std::string>()> pair  =  key >> -('=' >> value);
const qi::rule<std::string::const_iterator, std::unordered_map<std::string, std::string>()> query =  pair >> *((qi::lit("&amp;")|'&') >> pair);

它再次按预期工作,直到下一个案例 - a=b&d=e&f=$$g=h&i=j$$x=y&z=def,请注意,关闭“$$”和下一个之间没有符号键名。看起来可以通过添加像这样的 kleene 运算符来轻松解决

const qi::rule<std::string::const_iterator, std::unordered_map<std::string, std::string>()> query =  pair >> *(__*__(qi::lit("&amp;")|'&') >> pair);

但出于某种原因,它并不能解决问题。任何建议将不胜感激!

编辑: 示例代码

#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/adapted/std_pair.hpp>
#include <unordered_map>

namespace rulez
{
    using namespace boost::spirit::qi;
    using It = std::string::const_iterator;

    const rule<It, std::string()> key                                    = boost::spirit::qi::char_("a-zA-Z_") >> *boost::spirit::qi::char_("a-zA-Z_0-9/%\\-_~\\.");
    const rule<It, std::string()> escapedValue                           = boost::spirit::qi::omit["$$"] >> *(boost::spirit::qi::char_ - '$') >> boost::spirit::qi::omit["$$"];
    const rule<It, std::string()> value                                  = *(escapedValue | (boost::spirit::qi::char_ - '=' - '&'));
    const rule<It, std::pair<std::string, std::string>()> pair           = key >> -('=' >> value);
    const rule<It, std::unordered_map<std::string, std::string>()> query = pair >> *(*(boost::spirit::qi::lit("&amp;")|'&') >> pair);
}

int main()
{
    using namespace std;
    unordered_map<string, string> keyVal;
  //string const paramString = "a=b&d=e&f=$$g=h&i=j$$&x=y&z=def";
    string const paramString = "a=b&d=e&f=$$g=h&i=j$$x=y&z=def";

    boost::spirit::qi::parse(paramString.begin(), paramString.end(), rulez::query, keyVal);

    for (const auto& pair : keyVal)
        cout << "(\"" << pair.first << "\",\"" << pair.second << "\")" << endl;
}

“a=b&d=e&f=$$g=h&i=j$$x=y&z=def”的输出(错误,应该与“a=b&d=e&f=$$g=h&i=j”相同$$&x=y&z=def")

("a", "b"),("d", "e"),("f", "g=h&i=jx")

“a=b&d=e&f=$$g=h&i=j$$&x=y&z=def”的输出(如预期)

("a", "b"),("d", "e"),("f", "g=h&i=j"),("x", "y"),("z", "def")

编辑: 更简单的解析规则,只是为了让内容更容易理解

namespace rulez
{
    const rule<std::string::const_iterator, std::string()> key =  +(char_ - '&' - '=');
    const rule<std::string::const_iterator, std::string()> escapedValue = omit["$$"] >> *(char_ - '$') >> omit["$$"];
    const rule<std::string::const_iterator, std::string()> value = *(escapedValue | (char_ - '&' - '='));
    const rule<std::string::const_iterator, pair<std::string, std::string>()> pair  =  key >> -('=' >> value);
    const rule<std::string::const_iterator, unordered_map<std::string, std::string>()> query =  pair >> *(*(lit('&')) >> pair);
}

最佳答案

我猜你的问题是 value 规则

value = *(escapedValue | (char_ - '&' - '='));

解析时...$$g=h&i=j$$x=...

$$g=h&i=j$$x=
^---------^

它将标记的字符串 $$g=h&i=j$$ 解析为 escapedValue,然后 kleene 运算符 (*) 允许第二部分 (char_ - '&' - '=')value 规则解析 x

$$g=h&i=j$$x=
           ^

并且仅在 = 处规则停止。

也许这样的事情会有所帮助:

value = escapedValue | *(char_ - '&' - '=');

关于c++ - 使用 boost spirit 解析带有可选分隔符的字符串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21301777/

相关文章:

c++ - 将 boost.log 与 printf 样式的宏一起使用

c++ - 使用 Boost 库计算各种中心性

c++ - 如何从 Boost Spirit X3 词素解析器中获取字符串?

c++ - 如何在没有预标记的情况下解析异构列表?

c++ - 如何使用 boost::spirit 解析数学表达式并将其绑定(bind)到函数

c++ - Visual C++ 编译器为不明确的符号提供了错误的行引用

c++ - 获取最近的路径图

c++ - Netbeans C++ 不打印 UTF-8 字符

c++ - boost 日期不可变 - 那么如何在循环中使用它?

c++ - 使用 boost 正则表达式解析文本文件