【问题标题】:Parsing a string with optional separator using boost spirit使用增强精神解析带有可选分隔符的字符串
【发布时间】:2014-01-23 07:19:07
【问题描述】:

我正在尝试使用特殊规则解析 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 => 定义

所以我为“引用”值添加了附加规则

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 ", "定义")

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

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

【问题讨论】:

  • 提供可编译的示例(例如this)时更容易获得帮助。我不确定我是否理解您的意图,您期望的地图是map={{a,b}, {d,e}, {f,g=h&amp;i=j}, {x,y}, {z,def}};吗?
  • 你的value 规则应该是escapedValue|"unescapedValue",对吧?因为你现在拥有的是不同的。
  • 不,情况有点复杂,有四个规则,我们称它们为“key”、“value”、“pair”和“query”。实际上“value”应该看起来像(“query”|“unquotedvalue”),它创建了一种嵌套的“query”。我使用“未引用”而不是“未转义”来避免 url 转义混淆
  • 很抱歉,我很难理解您想问的问题。我建议它可能是具有预期输出的 SCCEE。最好更简单。
  • sehe,是的,我知道,重新阅读我的问题我意识到我很难清楚地定义问题,并且问题中充满了技术细节,这(可能)不太重要,我会尝试再次定义问题

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


【解决方案1】:

我猜你的问题是value 规则

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

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

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

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

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

只有在= 处,规则才会停止。

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

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

【讨论】:

  • 目前无法访问代码,但我有一种感觉,我必须将转义规则升级为 qi::omit["$$"] >> *( qi::char_ - '$') >> qi::omit["$$"] >> *(pair);
  • 那么,这个答案没有帮助吗?还在搜索吗?
  • 在我朋友的帮助下,这在提升精神上足够深刻,我找到了解决方案,但是我不想使用它
【解决方案2】:

这解决了问题。但是,我决定放弃使用 Spirit 解析查询字符串的想法——每种特殊情况都会使查询变得越来越麻烦,一段时间后,没有人会记得为什么查询是这样写的:)

qi::rule<std::string::const_iterator, std::string()> key =  +(qi::char_ - '=' - '&');
qi::rule<std::string::const_iterator, std::string()> escapedValue = qi::omit["$$"] >> *(qi::char_ - "$$") >> qi::omit["$$"];
qi::rule<std::string::const_iterator, std::string()> nonEscapedValue = !qi::lit("$$") >> *(qi::char_ - '=' - '&');

auto sep = qi::lit("&amp;") | '&';
qi::rule<std::string::const_iterator, std::pair<std::string, boost::optional<std::string>>()> keyValue = 
        key >> -('=' >> nonEscapedValue) >> (sep | qi::eoi);
qi::rule<std::string::const_iterator, std::pair<std::string, boost::optional<std::string>>()> escapedKeyValue =  
        key >> '=' >> escapedValue >> -(sep);
auto query = *(qi::hold[keyValue] | escapedKeyValue);

【讨论】:

    猜你喜欢
    • 2011-05-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多