C++ 为什么用Spirit解析一个空行会在map中产生一个空的键值对?
我正在尝试使用Spirit.Qi解析一个简单的文件格式,该格式的键值对用等号分隔。该文件还支持注释、空行以及引用值 我可以让几乎所有这些都按预期工作,但是,任何空行或注释都会导致将空的键值对添加到映射中。当地图被交易为矢量时,不会产生空白条目 示例程序: 输出:C++ 为什么用Spirit解析一个空行会在map中产生一个空的键值对?,c++,parsing,boost,key-value,boost-spirit,C++,Parsing,Boost,Key Value,Boost Spirit,我正在尝试使用Spirit.Qi解析一个简单的文件格式,该格式的键值对用等号分隔。该文件还支持注释、空行以及引用值 我可以让几乎所有这些都按预期工作,但是,任何空行或注释都会导致将空的键值对添加到映射中。当地图被交易为矢量时,不会产生空白条目 示例程序: 输出: 空的键值对来自哪里?我怎样才能防止它被生成呢?首先,你的程序有未定义的行为(事实上它在我的系统上崩溃了)。原因是您不能使用auto表达式来存储有状态解析器表达式 看,还有其他的。有关绕过此限制的有用策略,请参见例如 第二,空行是因为语法
空的键值对来自哪里?我怎样才能防止它被生成呢?首先,你的程序有未定义的行为(事实上它在我的系统上崩溃了)。原因是您不能使用
auto
表达式来存储有状态解析器表达式
看,还有其他的。有关绕过此限制的有用策略,请参见例如
第二,空行是因为语法
两者之间是有区别的
(-kvp) % qi::eol
或
第一个将导致“可选地解析kvp”,然后是“将结果推送到属性容器中”
后者可以选择“将1个或多个kvp解析到一个容器中”。请注意,如果不匹配,则不会推送空值
固定/演示
我建议
- 同时制作
和键
词素(实际上,只需在规则声明中删除跳过项);您可能不希望值
解析为“key name 1=value 1
。您可能也不想允许“keyname1”->“value1”
key#no value\n
- 使用BOOST_SPIRIT_DEBUG查看发生了什么
- 不使用命名空间boost::spirit覆盖
。这是个坏主意。相信我:/
- 规则声明可能看起来很冗长,但它们确实减少了规则定义中的繁琐之处
- 使用
而不是+eol
可以获得空行,这似乎是您想要的eol
#define BOOST_SPIRIT_DEBUG
#include "boost/spirit/include/qi.hpp"
#include "boost/spirit/include/karma.hpp"
#include "boost/fusion/include/std_pair.hpp"
#include <fstream>
#include <map>
namespace qi = boost::spirit::qi;
namespace karma = boost::spirit::karma;
template <typename It, typename Skipper, typename Data>
struct kvp_grammar : qi::grammar<It, Data(), Skipper> {
kvp_grammar() : kvp_grammar::base_type(start) {
using namespace qi;
value = raw [*print];
quoted_value = '"' >> *~char_('"') >> '"';
key = raw [ alpha >> *(alnum | '_') ];
kvp = key >> '=' >> (quoted_value | value);
start = -(kvp % +eol);
BOOST_SPIRIT_DEBUG_NODES((value)(quoted_value)(key)(kvp))
}
private:
using Pair = std::pair<std::string, std::string>;
qi::rule<It, std::string(), Skipper> value;
qi::rule<It, Pair(), Skipper> kvp;
qi::rule<It, Data(), Skipper> start;
// lexeme:
qi::rule<It, std::string()> quoted_value, key;
};
template <typename Map>
bool parse_vars(std::istream& is, Map& data) {
using It = boost::spirit::istream_iterator;
using Skipper = qi::rule<It>;
kvp_grammar<It, Skipper, Map> grammar;
It f(is >> std::noskipws), l;
Skipper skipper = ('#' >> *(qi::char_-qi::eol)) | qi::blank;
return qi::phrase_parse(f, l, grammar, skipper, data);
}
int main() {
std::ifstream ifs("input.txt");
std::map<std::string, std::string> vars;
if (parse_vars(ifs, vars)) {
std::cout << "vars[" << vars.size() << "]:" << std::endl;
std::cout << karma::format(*(karma::string << " -> " << karma::string << karma::eol), vars);
}
}
使用调试信息:
一=二\n三=四\n\n
一=二\n三=四\n\n
=two\n树=four\n\n#C
[o,n,e]]
二棵树=四棵树
二棵树=四棵树
\n树=四个\n\n#Comme
[t,w,o]]
\n树=四个\n\n#Comme
[o,n,e],[t,w,o]]
三=四\n\n#共
三=四\n\n#共
=四条\n\n#注释\n注释
[t,h,r,e,e]]
四个\n\n#注释\n五个
四个\n\n#注释\n五个
\n\n#注释\n五个=六个
[[f,o,u,r]]
\n\n#注释\n五个=六个
[t,h,r,e,e],[f,o,u,r]]
五=六\n
五=六\n
=六个\n
[f,i,v,e]]
六个\n
六个\n
\n
[s,i,x]]
\n
[f,i,v,e],[s,i,x]]
添加了一个演示,我知道不能在Spirit中使用auto
,但完全忘记了。谢谢你说得对,为什么我一直在地图上找一对空的。我还整合了你的其他评论。我想我的示例程序可能太简单了。再次感谢。如果你喜欢,我有一些关于解析INI文件的鼓舞人心的答案:;另一个或者你可以考虑
vars[4]:
->
one -> two
three -> four
five -> six
(-kvp) % qi::eol
-(kvp % qi::eol)
#define BOOST_SPIRIT_DEBUG
#include "boost/spirit/include/qi.hpp"
#include "boost/spirit/include/karma.hpp"
#include "boost/fusion/include/std_pair.hpp"
#include <fstream>
#include <map>
namespace qi = boost::spirit::qi;
namespace karma = boost::spirit::karma;
template <typename It, typename Skipper, typename Data>
struct kvp_grammar : qi::grammar<It, Data(), Skipper> {
kvp_grammar() : kvp_grammar::base_type(start) {
using namespace qi;
value = raw [*print];
quoted_value = '"' >> *~char_('"') >> '"';
key = raw [ alpha >> *(alnum | '_') ];
kvp = key >> '=' >> (quoted_value | value);
start = -(kvp % +eol);
BOOST_SPIRIT_DEBUG_NODES((value)(quoted_value)(key)(kvp))
}
private:
using Pair = std::pair<std::string, std::string>;
qi::rule<It, std::string(), Skipper> value;
qi::rule<It, Pair(), Skipper> kvp;
qi::rule<It, Data(), Skipper> start;
// lexeme:
qi::rule<It, std::string()> quoted_value, key;
};
template <typename Map>
bool parse_vars(std::istream& is, Map& data) {
using It = boost::spirit::istream_iterator;
using Skipper = qi::rule<It>;
kvp_grammar<It, Skipper, Map> grammar;
It f(is >> std::noskipws), l;
Skipper skipper = ('#' >> *(qi::char_-qi::eol)) | qi::blank;
return qi::phrase_parse(f, l, grammar, skipper, data);
}
int main() {
std::ifstream ifs("input.txt");
std::map<std::string, std::string> vars;
if (parse_vars(ifs, vars)) {
std::cout << "vars[" << vars.size() << "]:" << std::endl;
std::cout << karma::format(*(karma::string << " -> " << karma::string << karma::eol), vars);
}
}
vars[3]:
five -> six
one -> two
three -> four