[英]How can I parse a bracketed string to a list of string with a given delimitor
我的任务是解析一个带括号的字符串,比如
[foo | bar | foobar]
[foo | bar | foobar]
,到std::strings
的vector
。
在这种情况下, 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);
注意:如果您有 C++14,请考虑使用 X3: Live On Coliru 。 这样编译会快很多
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.