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

问题描述

我的任务是解析方括号中的字符串,例如

[foo | bar | foobar]vector中的std::strings

在这种情况下, 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(),'[' 
    >> qi::lexeme[ +~qi::char_("|]") ] % '|' 
    >> ']',res);

它将解析:

"foo "
"bar "
"foobar"

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

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

查看 Live On Coliru

注意:如果您使用的是C ++ 14,请考虑使用X3:Live On Coliru。编译起来会快很多

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...