Я изо всех сил пытался найти способ в C ++ получить токены и параметры в моем собственном файле сценария, я могу сделать это в C # с помощью регулярных выражений и все, но мне нужно сделать это в C ++
Допустим, у меня есть этот файл сценария:
Name = Tywin Lanninster
Age = 35
CustomAttributes = {
Health = 100,
Mana = 100,
Height = 4,
Weight = 40
}
Что мне нужно знать о том, как взаимодействовать через весь этот код и запускать между токенами?
Как получить значение имени, возрастное значение, а затем получить все данные в поведении, а также получить его значение? Это похоже на то, что я могу разобрать это, я придаю импульс, но Дух не то, что я ищу, я видел std::string
имеет find_first_of
функция, поэтому я ищу ответы.
Я нашел вопрос, подобный моему, от 2007 года, но приведенный код не является достаточным, поскольку он неполон, но я вижу, что это то, что я ищу: http://www.daniweb.com/software-development/cpp/threads/76797/simple-script-parser-how-to-
Пожалуйста, помогите мне!
Итак, я сделал это:
Это мой первый цикл загрузки для разбора:
while (getline(file, text)) {
if (!getNextToken()) continue;
std::cout << "Token: '" << token << "'" << std::endl;
if (token == "Name")
{
getString();
}
else {
std::cout << "Unknown token: '" << token << "'" << std::endl;
}
}
Функция, которую я использую для получения токенов, позволяет получать такие вещи, как Age & название
bool Script::getNextToken()
char letter;
for (int i = 0; i != text.size(); i++)
{
letter = text[i];
if (letter == TOKEN_EQUAL)
{
token = text.substr(0, i-1);
text.erase(0, i+2);
return true;
}
}
return false;
}
Код, который я использую для получения строк внутри «
bool Script::getString()
char letter;
std::string str;
std::cout << "Trying to get string from: " << text << std::endl;
for (int i = 0; i != text.size() + 1; i++)
{
letter = text[i];
if (letter == TOKEN_STRING)
{
text.erase(0, 1);
int pos = text.find_first_of(TOKEN_STRING);
str = text.substr(i, pos);
std::cout << "String Found: " << str << std::endl;
text.erase(pos);
std::cout << "text left: " << text << std::endl;
break;
}
}
Но я не знаю, как получить вещи внутри {}, так как они в разных строках, и я использую getline
Учитывая, что ваш формат файла немного сложнее, чем я думал, я сделал еще один снимок, используя подталкивание-спирит этот раз. Действительно, это утомительное предприятие, чтобы заставить его работать с чем-то более сложным, чем тривиальные примеры из учебника, но с небольшой помощью записей в стеке потока мне удалось заставить что-то работать.
Здесь это идет:
#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;
}
}
Несколько моментов:
Для этого я использовал версию Boost 1.49.
Приведенный выше код работает с образцом, представленным в вопросе, если он хранится в файле (точно названном player.txt
) , а также с двойными кавычками вокруг строк, которые должны быть значениями, а не ключевые слова, как, например, значение для название приписывать.
Я решил разделить грамматику на две части, чтобы вы могли повторно использовать и расширять раздел атрибутов. Кажется разумным предположение о том, куда вы направитесь дальше. Однако это означает, что поддержка подталкивание-фьюжн сериализация будет сложно. В 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
должен быть класс, который вы определили, который содержит данные, которые вы проанализировали. Вы должны получить идею. Сейчас пожалуйста, попробуйте написать что-нибудь и вернуться, если вы застряли.
Замечания: вышеуказанный код не проверен.