我有一个包含表单数据的文件:
fractal mand1 {
;lkkj;kj;
}
fractal mand2 {
if (...) {
blablah;
}
}
fractal julia1 {
a = ss;
}
我想提取数据容器的名称,所以我想在特定情况下检索包含 mand1
、mand2
、julia1
.
我已经阅读了有关 parsing a number list into a vector 的示例, 但我想在单独的文件中维护语法。
我创建了一个表示语法的结构,然后使用它来解析包含数据的字符串。我希望输出像
mand1
mand2
julia1
取而代之的是
mand1 {
;lkkj;kj;
}
fractal mand2 {
if (...) {
blablah;
}
}
fractal julia1 {
a = ss;
}
我的解析器识别第一个 fractal
术语,但随后它将文件的其余部分解析为单个字符串项,而不是按我的需要解析它。
我做错了什么?
#include <boost/spirit/include/qi.hpp>
#include <string>
#include <vector>
#include <iostream>
using boost::spirit::ascii::space;
using boost::spirit::ascii::space_type;
using boost::spirit::qi::phrase_parse;
using boost::spirit::qi::lit;
using boost::spirit::qi::lexeme;
using boost::spirit::qi::skip;
using boost::spirit::ascii::char_;
using boost::spirit::ascii::no_case;
using boost::spirit::qi::rule;
typedef std::string::const_iterator sit;
template <typename Iterator>
struct FractalListParser : boost::spirit::qi::grammar<Iterator, std::vector<std::string>(), boost::spirit::ascii::space_type> {
FractalListParser() : FractalListParser::base_type(start) {
no_quoted_string %= *(lexeme[+(char_ - '"')]);
start %= *(no_case[lit("fractal")] >> no_quoted_string >> '{' >> *(skip[*(char_)]) >> '}');
}
rule<Iterator, std::string(), space_type> no_quoted_string;
rule<Iterator, std::vector<std::string>(), space_type> start;
};
int main() {
const std::string fractalListFile(R"(
fractal mand1 {
;lkkj;kj;
}
fractal mand2 {
if (...) {
blablah;
}
}
fractal julia1 {
a = ss;
}
)");
std::cout << "Read Test:" << std::endl;
FractalListParser<sit> parser;
std::vector<std::string> data;
bool r = phrase_parse(fractalListFile.begin(), fractalListFile.end(), parser, space, data);
for (auto& i : data) std::cout << i << std::endl;
return 0;
}
最佳答案
如果你使用错误处理,你会发现解析失败,没有任何有效的解析:
输出:
Read Test:
Parse success:
----
mand1 {
;lkkj;kj;
}
fractal mand2 {
if (...) {
blablah;
}
}
fractal julia1 {
a = ss;
}
Remaining unparsed input: 'fractal mand1 {
;lkkj;kj;
}
fractal mand2 {
if (...) {
blablah;
}
}
fractal julia1 {
a = ss;
}
'
问题是什么?
您可能想要忽略“正文”(在
{}
之间)。因此我想您实际上想要省略
属性:>> '{' >> *(omit[*(char_)]) >> '}'
而不是
skip(*char_)
。表达式
*char_
是贪婪的,并且总是匹配到输入的末尾......你可能想限制字符集:在“name”
*~char_("\"{")
中也可以避免“吃掉”整个 body 。要避免匹配空格,请使用graph
(例如+graph - '"'
)。如果您想明确解析“标识符”,例如alpha > *(alnum | char_('_'))
在主体
*~char_('}')
或*(char_ - '}')
(后者效率较低)。
可选量词的嵌套效率不高:
*(omit[*(char_)])
只会有非常慢的最坏情况运行时间(因为
*char_
可能为空,而*(omit[*(char_)])
也可能为空) .说出你的意思:omit[*char_]
拥有词位的最简单方法是从规则声明中删除 skipper (另请参阅 Boost spirit skipper issues)
程序逻辑:
由于您的示例包含嵌套 block (例如
mand2
),您需要递归地处理这些 block 以避免调用第一个的结尾外 block :
block = '{' >> -block % (+~char_("{}")) >> '}';
松散的提示:
使用
BOOST_SPIRIT_DEBUG
找出解析被拒绝/匹配的位置。例如。稍微重构规则后:我们得到了输出(On Coliru):
Read Test: <start> <try>fractal mand1 {\n </try> <no_quoted_string> <try>mand1 {\n ;lkkj;kj</try> <success> {\n ;lkkj;kj;\n}\n\n</success> <attributes>[[m, a, n, d, 1]]</attributes> </no_quoted_string> <body> <try>{\n ;lkkj;kj;\n}\n\nf</try> <fail/> </body> <success>fractal mand1 {\n </success> <attributes>[[]]</attributes> </start> Parse success: Remaining unparsed input: 'fractal mand1 { ;lkkj;kj; } fractal mand2 { if (...) { blablah; } } fractal julia1 { a = ss; } '
该输出帮助我发现我实际上忘记了正文规则中的
- '}'
部分...:)当规则定义中不涉及任何语义操作时,不需要
%=
(docs)您可能想确保
fractal
实际上是一个单独的词,因此您不匹配fractalset multi { .... }
演示程序
有了这些,我们就可以有一个工作演示:
//#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <iostream>
namespace qi = boost::spirit::qi;
template <typename Iterator>
struct FractalListParser : qi::grammar<Iterator, std::vector<std::string>(), qi::space_type> {
FractalListParser() : FractalListParser::base_type(start) {
using namespace qi;
identifier = alpha > *(alnum | char_('_'));
block = '{' >> -block % +~char_("{}") >> '}';
start = *(
no_case["fractal"] >> identifier >> block
);
BOOST_SPIRIT_DEBUG_NODES((start)(block)(identifier))
}
qi::rule<Iterator, std::vector<std::string>(), qi::space_type> start;
// lexemes (just drop the skipper)
qi::rule<Iterator, std::string()> identifier;
qi::rule<Iterator> block; // leaving out the attribute means implicit `omit[]`
};
int main() {
using It = boost::spirit::istream_iterator;
It f(std::cin >> std::noskipws), l;
std::cout << "Read Test:" << std::endl;
FractalListParser<It> parser;
std::vector<std::string> data;
bool r = qi::phrase_parse(f, l, parser, qi::space, data);
if (r) {
std::cout << "Parse success:\n";
for (auto& i : data)
std::cout << "----\n" << i << "\n";
} else {
std::cout << "Parse failed\n";
}
if (f != l)
std::cout << "Remaining unparsed input: '" << std::string(f,l) << "'\n";
}
打印:
Read Test:
Parse success:
----
mand1
----
mand2
----
julia1
关于c++ - 创建用于检索字符串 vector 的语法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33974879/