C++ 为什么用Spirit解析一个空行会在map中产生一个空的键值对?

C++ 为什么用Spirit解析一个空行会在map中产生一个空的键值对?,c++,parsing,boost,key-value,boost-spirit,C++,Parsing,Boost,Key Value,Boost Spirit,我正在尝试使用Spirit.Qi解析一个简单的文件格式,该格式的键值对用等号分隔。该文件还支持注释、空行以及引用值 我可以让几乎所有这些都按预期工作,但是,任何空行或注释都会导致将空的键值对添加到映射中。当地图被交易为矢量时,不会产生空白条目 示例程序: 输出: 空的键值对来自哪里?我怎样才能防止它被生成呢?首先,你的程序有未定义的行为(事实上它在我的系统上崩溃了)。原因是您不能使用auto表达式来存储有状态解析器表达式 看,还有其他的。有关绕过此限制的有用策略,请参见例如 第二,空行是因为语法

我正在尝试使用Spirit.Qi解析一个简单的文件格式,该格式的键值对用等号分隔。该文件还支持注释、空行以及引用值

我可以让几乎所有这些都按预期工作,但是,任何空行或注释都会导致将空的键值对添加到映射中。当地图被交易为矢量时,不会产生空白条目

示例程序: 输出:
空的键值对来自哪里?我怎样才能防止它被生成呢?

首先,你的程序有未定义的行为(事实上它在我的系统上崩溃了)。原因是您不能使用
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