首页 > 解决方案 > Boost::qi 解析字符串

问题描述

我需要从下一个 hls 标签中解析“title”

标签的模式:#EXTINF:<duration>[,<title>]

以真实标签为例:

#EXTINF:10,Title of the segment => I need "Title of the segment" phrase

#EXTINF:20,Title => I need "Title" phrase

#EXTINF:12 => I need "" phrase

我写了下一个代码

double duration;
std::string title;

boost::spirit::qi::rule<Iterator, std::string()> quoutedString;
quoutedString %= lexeme[+(char_)];

bool r = parse(first, last,
    ("#EXTINF:" >> double_[ref(duration) = _1] >> -(',' >> quoutedString[ref(title) = _1] ) )
);
if (!r || first != last) {
    addMinorProblem(stateObj, _("Cannot parse information from #EXTINF tag"));
    return false;
}

但是我在编译过程中遇到了下一个错误:

error: call of overloaded ‘ref(std::__cxx11::string&)’ is ambiguous
         ("#EXTINF:" >> double_[ref(duration) = _1] >> -(',' >> quoutedString[ref(title) = _1] ) )

请帮我。我究竟做错了什么?

标签: c++c++11boostboost-spiritboost-spirit-qi

解决方案


您使用的命名空间太多了。此外,ADL无论如何std::ref都会引入std::string参数,除非ref是带括号的或命名空间限定的。

过度使用using namespaceis 从来都不是一个好主意(参见例如为什么“使用命名空间 std;”被认为是不好的做法?std::ref ),在这种情况下,该消息说明了and之间的混淆boost::phoenix::ref(以及可能的其他人,但您没有包括完整的消息)。

拒绝吧:

住在科利鲁

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/optional.hpp>
#include <iomanip>

namespace qi = boost::spirit::qi;
namespace px = boost::phoenix;

#define addMinorProblem(...) do {} while (0)

boost::optional<std::pair<double, std::string>> parse(std::string_view input)
{
    using Iterator = std::string_view::const_iterator;

    Iterator    first = begin(input), last = end(input);
    double      duration;
    std::string title;

    boost::spirit::qi::rule<Iterator, std::string()> quoutedString;
    quoutedString %= qi::lexeme[+(qi::char_)];

    bool r = parse(first, last,
                   ("#EXTINF:" >> qi::double_[px::ref(duration) = qi::_1] >>
                    -(',' >> quoutedString[px::ref(title) = qi::_1])));
    if (!r || first != last) {
        addMinorProblem(stateObj,
                        _("Cannot parse information from #EXTINF tag"));
        return {};
    }

    return std::make_pair(duration, title);
}

int main()
{
    for (std::string const input : {
             "#EXTINF:10,Title of the segment", // => I need "Title of the
                                                // segment" phrase
             "#EXTINF:20,Title",                // => I need "Title" phrase
             "#EXTINF:12",                      // => I need "" phrase
         }) {
        if (auto result = parse(input)) {
            std::cout << "Parsed: (" << result->first << ", " << std::quoted(result->second) << ")\n";
        } else {
            std::cout << "Cannot parse " << std::quoted(input) << "\n";
        }
    }
}

印刷

Parsed: (10, "Title of the segment")
Parsed: (20, "Title")
Parsed: (12, "")

改善,小事

  1. 通过一些明智的本地using 声明,您可以再次使其“更短”:

    using namespace qi::labels;
    using px::ref;
    bool r = parse(first, last,
                   ("#EXTINF:" >> qi::double_[ref(duration) = _1] >>
                    -(',' >> quoutedString[(ref)(title) = _1])));
    

    我个人觉得这更晦涩难懂(想象自己(ref)(title)在代码审查中解释?)

  2. operator %=没有语义动作是没有意义的

  3. qi::lexeme[]没有船长就毫无意义(请参阅Boost Spirit 船长问题

  4. quoutedString [原文如此]用词不当(现在?)因为它不解析引号

  5. 为什么不使用自动属性传播而不是痛苦的语义操作?它们只会增加编译时间,而且,正如您所发现的,还会增加开发时间。参见Boost Spirit:“语义行为是邪恶的”?

  6. first == last另外,与其在解析后进行繁琐的检查,不如简单地匹配>> qi::eoi表达式?

以上所有内容都简化为以下内容:

住在科利鲁

boost::optional<std::pair<double, std::string>> parse(std::string_view input)
{
    namespace qi = boost::spirit::qi;
    double duration;
    std::string title;

    if (qi::parse(
        begin(input), end(input), //
        ("#EXTINF:" >> qi::double_ >> -(',' >> +qi::char_) >> qi::eoi),
        duration, title))
    {
        return std::make_pair(duration, title);
    }

    addMinorProblem(stateObj, _("Cannot parse information from #EXTINF tag"));
    return {};
}

没有更多phoenix,语义动作,什么不是。仍在打印:

Parsed: (10, "Title of the segment")
Parsed: (20, "Title")
Parsed: (12, "")

推荐阅读