更新:
鉴于您的文件格式比我最初想象的要复杂一些,我这次使用boost-spirit 再试一次。确实,让它与比教程中的简单示例更复杂的东西一起工作是一件乏味的事情,但在 stackoverflow 条目的一些帮助下,我设法让一些东西工作。
这里是:
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_object.hpp>
#include <boost/spirit/include/support_istream_iterator.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>
#include <iostream>
#include <fstream>
#include <string>
namespace data {
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
struct custom_attributes {
/*
custom_attributes(int h = 0, int m = 0, int ht = 0, int w = 0):
health(h), mana(m), height(ht), weight(w){}
*/
int health;
int mana;
int height;
int weight;
};
struct player {
/*
player(std::string n="", int a=0, custom_attributes at={}):
name(n), age(a), attrs(at) {}
*/
std::string name;
int age;
custom_attributes attrs;
};
}
BOOST_FUSION_ADAPT_STRUCT(
data::custom_attributes,
(int, health)
(int, mana)
(int, height)
(int, weight)
)
BOOST_FUSION_ADAPT_STRUCT(
data::player,
(std::string, name)
(int, age)
(data::custom_attributes, attrs)
)
namespace data {
template <typename Iterator>
struct attributes_parser : qi::grammar<Iterator, custom_attributes(), ascii::space_type> {
attributes_parser() : attributes_parser::base_type(start) {
using qi::int_;
using qi::lit;
using qi::lexeme;
using ascii::char_;
using ascii::space_type;
start %=
lit("CustomAttributes")
>> '=' >> '{'
>> lit("Health") >> '=' >> int_ >> ','
>> lit("Mana") >> '=' >> int_ >> ','
>> lit("Height") >> '=' >> int_ >> ','
>> lit("Weight") >> '=' >> int_
>> '}'
;
}
qi::rule<Iterator, custom_attributes(), ascii::space_type> start;
};
template <typename Iterator>
struct player_parser : qi::grammar<Iterator, player(), ascii::space_type> {
player_parser() : player_parser::base_type(start) {
using qi::int_;
using qi::lit;
using qi::lexeme;
using ascii::char_;
using ascii::space_type;
quoted_string %= lexeme[+(char_ - '"')];
start %=
lit("Name") >> '=' >> '"' >> quoted_string >> '"' >>
lit("Age") >> '=' >> int_ >> attributes
;
}
qi::rule<Iterator, std::string(), ascii::space_type> quoted_string;
qi::rule<Iterator, player(), ascii::space_type> start;
attributes_parser<Iterator> attributes;
};
}
int main(){
using boost::spirit::ascii::space;
typedef boost::spirit::istream_iterator iterator_type;
typedef data::player_parser<iterator_type> player_parser;
player_parser g;
data::player plr;
std::ifstream in("player.txt");
in.unsetf(std::ios::skipws);
iterator_type iter(in), end;
bool r = phrase_parse(iter, end, g, space, plr);
if (r && iter == end){
std::cout << "parsing succeeded:" << std::endl;
std::cout << " name = " << plr.name << std::endl;
std::cout << " age = " << plr.age << std::endl;
std::cout << " health = " << plr.attrs.health << std::endl;
std::cout << " mana = " << plr.attrs.mana << std::endl;
std::cout << " height = " << plr.attrs.height << std::endl;
std::cout << " weight = " << plr.attrs.weight << std::endl;
} else {
std::cout << "parsing failed" << std::endl;
}
}
几点:
我为此使用了 1.49 版的 boost。
上面的代码适用于问题中提供的示例,如果存储在文件中(恰当地命名为player.txt),并且在字符串周围带有双引号,而不是关键字,例如 Name 属性的值。
我选择将语法一分为二,以便您可以重用和扩展属性部分。对于你接下来要去哪里,这似乎是一个合理的猜测。但是,这意味着对boost-fusion 序列化 的支持会很困难。 stackoverflow 上有处理该问题的条目。请检查一下,如果有问题,请再次询问。
旧答案:
既然你要使用正则表达式,我想文档的语法很简单,你想逐行阅读。此外,您在问题中提供的示例可以用空格或回车作为分隔符进行标记。
您可以简单地使用 iostream 和 >> 运算符,以及用于每种行格式的一组组合器(即,执行每个简单操作的函数,您可以组合起来产生更复杂的操作)。
#include <iostream>
#include <stdexcept>
#include <vector>
using std::string;
using std::istream;
using std::vector;
class parse_exn : public std:runtime_error{
public:
parse_exn(const string msg) : runtime_error(msg){}
};
bool expect(istream &is, const string atom){
string data;
Is >> data;
return is.good() && data == atom; // or throw
}
int readAttribute(istream &is, const string name){
int result;
if (expect(is, name) && expect(is,"="))
is >> result;
If (is.good())
return result;
}
throw parse_exn("invalid attribute");
}
Yourclass readStruct(istream &is, const vector<string> attributes){
// parse header (name, "=","{")
// parse each attribute and commas
// parse footer ("}")
// return the object
// or throw an exn if something got wrong
}
Yourclass 应该是您定义的包含您解析的数据的类。你应该明白了。现在请尝试写一些东西,如果遇到困难,请回来。
注意:以上代码未经测试。