为什么使用Spirit解析空行会在地图中生成空键值对?

时间:2015-04-08 05:30:54

标签: c++ parsing boost key-value boost-spirit

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

我几乎可以按预期工作,但是,任何空白行或注释都会导致将空键值对添加到地图中。交换地图时,不会生成任何空白条目。

示例程序:

#include <fstream> 
#include <iostream> 
#include <string> 
#include <map> 

#include "boost/spirit/include/qi.hpp" 
#include "boost/spirit/include/karma.hpp" 
#include "boost/fusion/include/std_pair.hpp" 

using namespace boost::spirit; 
using namespace boost::spirit::qi; 

//////////////////////////////////////////////////////////////////////////////// 
int main(int argc, char** argv) 
{ 
   std::ifstream ifs("file"); 
   ifs >> std::noskipws; 

   std::map< std::string, std::string > vars; 

   auto value = as_string[*print]; 
   auto quoted_value = as_string[lexeme['"' >> *(print-'"') >> '"']]; 
   auto key = as_string[alpha >> *(alnum | char_('_'))]; 
   auto kvp = key >> '=' >> (quoted_value | value); 

   phrase_parse( 
      istream_iterator(ifs), 
      istream_iterator(), 
      -kvp % eol, 
      ('#' >> *(char_-eol)) | blank, 
      vars); 

   std::cout << "vars[" << vars.size() << "]:" << std::endl; 
   std::cout << karma::format(*(karma::string << " -> " << karma::string << karma::eol), vars); 

   return 0; 
}

输入文件:

one=two
three=four

# Comment
five=six

输出:

vars[4]:
 ->
one -> two
three -> four
five -> six

空键值对来自何处?我怎样才能防止它被生成?

1 个答案:

答案 0 :(得分:1)

首先,您的程序有未定义的行为(实际上它在我的系统上崩溃)。原因是您不能使用auto表达式来存储有状态解析器表达式。

  

请参阅Assigning parsers to auto variablesboost spirit V2 qi bug associated with optimization level和其他人。参见例如these answers提供了解决此限制的有用策略。

其次,空行是因为语法。

之间存在差异
  (-kvp) % qi::eol

  -(kvp % qi::eol)

第一个将导致&#34;可选地解析kvp&#34;然后按&#34;将结果推送到属性容器&#34;。

后者可选地&#34;将1个或更多kvp解析为容器&#34;。请注意,如果空值不匹配,则不会推送空值。

固定/演示

我建议

  • 同样制作keyvalue词句(只需将Skipper放在规则声明上,真的);您可能不希望'key name 1=value 1解析为"keyname1" -> "value1"。您可能也不想允许key # no value\n
  • 使用BOOST_SPIRIT_DEBUG查看正在进行的操作
  • 不是毯子using namespace boost::spirit。这是个坏主意。相信我:/
  • 规则声明可能看起来很冗长,但是会减少规则定义中的错误
  • 使用+eol代替eol允许空行,这似乎是您想要的

<强> Live On Coliru

#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); 
    }
}

输出(目前在Coliru上打破):

vars[3]:
five -> six
one -> two
three -> four

使用调试信息:

<kvp>
  <try>one=two\nthree=four\n\n</try>
  <key>
    <try>one=two\nthree=four\n\n</try>
    <success>=two\nthree=four\n\n# C</success>
    <attributes>[[o, n, e]]</attributes>
  </key>
  <quoted_value>
    <try>two\nthree=four\n\n# Co</try>
    <fail/>
  </quoted_value>
  <value>
    <try>two\nthree=four\n\n# Co</try>
    <success>\nthree=four\n\n# Comme</success>
    <attributes>[[t, w, o]]</attributes>
  </value>
  <success>\nthree=four\n\n# Comme</success>
  <attributes>[[[o, n, e], [t, w, o]]]</attributes>
</kvp>
<kvp>
  <try>three=four\n\n# Commen</try>
  <key>
    <try>three=four\n\n# Commen</try>
    <success>=four\n\n# Comment\nfiv</success>
    <attributes>[[t, h, r, e, e]]</attributes>
  </key>
  <quoted_value>
    <try>four\n\n# Comment\nfive</try>
    <fail/>
  </quoted_value>
  <value>
    <try>four\n\n# Comment\nfive</try>
    <success>\n\n# Comment\nfive=six</success>
    <attributes>[[f, o, u, r]]</attributes>
  </value>
  <success>\n\n# Comment\nfive=six</success>
  <attributes>[[[t, h, r, e, e], [f, o, u, r]]]</attributes>
</kvp>
<kvp>
  <try>five=six\n</try>
  <key>
    <try>five=six\n</try>
    <success>=six\n</success>
    <attributes>[[f, i, v, e]]</attributes>
  </key>
  <quoted_value>
    <try>six\n</try>
    <fail/>
  </quoted_value>
  <value>
    <try>six\n</try>
    <success>\n</success>
    <attributes>[[s, i, x]]</attributes>
  </value>
  <success>\n</success>
  <attributes>[[[f, i, v, e], [s, i, x]]]</attributes>
</kvp>
<kvp>
  <try></try>
  <key>
    <try></try>
    <fail/>
  </key>
  <fail/>
</kvp>