繁体   English   中英

如何将括号中的字符串解析为具有给定分隔符的字符串列表

[英]How can I parse a bracketed string to a list of string with a given delimitor

我的任务是解析一个带括号的字符串,比如

[foo | bar | foobar] [foo | bar | foobar] ,到std::stringsvector

在这种情况下, vector应该以内容{"foo" , "bar", "foobar"}结尾。

这些括号可以嵌套。 例如,给定的括号字符串

[[john | doe] | [ bob | dylan]]

会变成{ "[john | doe]" , "[bob | dylan] } "

到目前为止我能做到的最好的是

int main(int argc, char ** argv)
{
    const std::string input {argv[1]};
    std::vector<std::string> res;

    qi::phrase_parse(input.cbegin(), input.cend(),
     '[' 
     >> *qi::lexeme[ +(qi::char_ - '|')  >> '|'] 
     > -qi::lexeme[ +(qi::char_  - ']') >> ']' ],
     qi::space ,
     res);

    for (const auto& v: res)
        std::cout << v  <<std::endl;

    return 0;
}

对于嵌套的情况,它失败了。 有人可以指出我正确的方向吗?

注意#1:嵌套案例可以不止一个。

注意#2:我欢迎任何更简单的解决方案,即使不使用 Boost Spirit。

这是一个简单的 C++ 解析器,它基于括号平衡的假设,即每个[都有一个]

bracket是左括号的数量。 当该数字为1时,我们会做出关键决定。

#include <iostream>
#include <vector>
#include <string>
#include <string_view>

bool edge(const int num){
    return num == 1;
}

int main(){
    std::vector<std::string> all;
    std::string line;
    // std::getline(std::cin, line);
    line = "[[john | doe] | [ bob | dylan]]";

    int bracket = 0;
    std::string::size_type start = 0;
    for(int i = 0; i < line.size(); i++){
        const char c = line[i];
        if(c == '['){
            bracket++;
            if(edge(bracket)){
                start = i + 1;
            }
        }
        if(c == ']'){
            if(edge(bracket)){
                all.push_back(line.substr(start, i - start));
            }
            bracket--;
        }
        if(c == '|' && edge(bracket)){
            all.push_back(line.substr(start, i - start));
            start = i + 1;
        }
    }
    for(std::string_view t : all){
        std::cout << t << std::endl;
    }
}

如果您想要嵌套的字符串列表,首先您需要一个可以存储嵌套列表的结果。 幸运的是,在 C++17 中,您可以拥有前向引用向量(只要它们在某个时候定义)。 因此,您可以创建一个列表类型,其中每个项目都是字符串或另一个列表:

struct Expr : std::vector<
    boost::variant<
        std::string,
        Expr>>
{ 
    using std::vector<boost::variant<std::string, Expr>>::vector;
};

后面的语法很简单。 请注意,它是递归的 - Term可以嵌套一个Expr

WORD = /[^\[\|\]]+/
Term = WORD | Expr
Expr = '[' Term ('|' Term)* ']';

您可以分别表达每个规则。 Boost Spirit Qi 方便地有%运算符,它解析一个分隔列表并将其插入到容器中。

using It = std::string::const_iterator;
using Sk = qi::space_type;
qi::rule<It, std::string(), Sk> word;
qi::rule<It, boost::variant<std::string, Expr>(), Sk> term;
qi::rule<It, Expr(), Sk> expr;

word = +(qi::char_ - '[' - '|' - ']'); 
term = word | expr;
expr = '[' >> (term % '|') >> ']';

然后qi::phrase_parse会做你想做的:

Expr res;
qi::phrase_parse(input.cbegin(), input.cend(), expr, qi::space, res);

演示: https : //godbolt.org/z/5W993s

这个更简单的版本似乎是你想要的:

qi::phrase_parse(input.cbegin(), input.cend(),
    '[' 
    >> qi::lexeme[ +~qi::char_("|]") ] % '|' 
    >> ']',
    qi::space,
    res);

它会解析:

"foo "
"bar "
"foobar"

也许您实际上并不希望将空格作为匹配的一部分。 那么它可以更简单:

qi::phrase_parse(input.cbegin(), input.cend(),
    '[' 
    >> qi::lexeme[ +(qi::graph - qi::char_("|]")) ] % '|' 
    >> ']',
    qi::space,
    res);

在 Coliru 上观看直播

注意:如果您有 C++14,请考虑使用 X3: Live On Coliru 这样编译会快很多

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM