Скачать исходники
Это класс для разбора текстового файла вида:
Name1
Name2 = "TextValue"
Name3=123.2
Также строка игнорируется, если она начинается со знака ";".
Класс сам определяет тип значения (справа от знака равно), причем можно добавлять свои типы. Вот его заголовочный файл (только существенные моменты):
#pragma once
#include <STRING>
#include <FSTREAM>
#include <ios>
using std::string;
using std::fstream;
using std::ios;
/*-------------------------------------
Считаем, что файл может выглядить так:
; Комментарий
Name1 = "11111"
; Комментарий
Name2=123.44
Name3= 111
Name4
Name5 ="1111"
---------------------------------------*/
#define ERR_OPENFILE 1
#define ERR_TYPE 2
#define ERR_NOTCREATE 3
#define ERR_ENDOFFILE 4
// Тип значения строки
enum StringType
{
SD_BASE = -2,
ST_ERROR = -1, // Невозможно определить тип значения
ST_NAMEONLY = 0, // нет значения, есть только название
ST_STRING = 1, // Значение - строка
ST_INT = 2, // Значение - целое число
ST_FLOAT = 3 // Значение - дробное число
};
enum OpenType // Тип открытого парсера
{
OT_NONE = 0,
OT_READ,
OT_WRITE
};
// Структура для заполнения значением строки
class StringData
{
public:
string Name; // Название значения
StringType Type; // Тип значения
// Заполняется одно из этих полей в зависимости от Type
string strVal;
int intVal;
double fVal;
virtual StringType GetType() { return SD_BASE; }; // Чтобы можно было различать типы
...
};
class CTextParser
{
'''public:
protected:'''
...
'''// Чтобы можно было потом добавить еще новые типы
// Функция возвращает true, если тип узнала'''
virtual bool CustomType(string str, StringData *info) { return false; };
unsigned int m_CurLine; // Текущая строка
...
public:
CTextParser();
virtual ~CTextParser();
OpenType Create(const char *fname, bool Write, bool Overwrite = false); // С этого надо начинать
void Destroy(); // А этим кончать (но не обязательно)
bool GetNextValue(StringData *info); // Получить следующую строку (пустые строки пропускает)
// Функции для записи в файл
bool Write (string name, string val);
bool Write (string name, int val);
bool Write (string name, float val);
bool Write (string name, double val);
bool Write (StringData *info);
bool WriteComment(string str);
...
}
Парсер может как читать файл, так и писать в него. Чтобы воспользоваться TextParser, надо создать его экземпляр и вызвать метод Create(). Первый параметр - это имя файла, с которым хотим работать, дальше идет переменная типа bool, которая равна true, если мы хотим писать в файл, и false, если надо обработать файл, то есть пропарсить. А последний параметр указывает надо ли перезаписывать файл при открытии на запись, если файл с таким именем уже существует. Метод возвращает тип OpenType, которые обозначает, как открыли файл, на чтение, на запить, или открыть файл не удалось (тогда возвращается значение OT_NONE.
Вся суть класса заключена в методе GetNextValue. Он возвращает true, если разбор очередной строки прошел удачно, и false в противном случае (возможно файл закончился). Также этот метод заполняет класс типа StringData, который используется как структура. Он объявлен следующим образом:
// Структура для заполнения значением строки
class StringData
{
public:
string Name; // Название значения
StringType Type; // Тип значения
// Заполняется одно из этих полей в зависимости от Type
string strVal;
int intVal;
double fVal;
virtual StringType GetType() { return SD_BASE; }; // Чтобы можно было различать типы
...
};
Этот класс содержит только открымые метода и члены. Член Name содержит имя параметра (то, что стоит слева от знака равно). Член Type определяет тип значения. В исходном варианте (просто класс можно расширять) может принимать следующие значения:
enum StringType
{
SD_BASE = -2,
ST_ERROR = -1, // Невозможно определить тип значения
ST_NAMEONLY = 0, // нет значения, есть только название
ST_STRING = 1, // Значение - строка
ST_INT = 2, // Значение - целое число
ST_FLOAT = 3 // Значение - дробное число
};
Ну а с функциями Write там и так все понятно, для их использования надо открыть файл, естественно, на запись.
После использоваться надо вызвать метод Destroy(), но это не обязательно (если только вы не захотите работать с другим файлом), он вызывается в деструкторе.
Класс можно расширять, т.е. добавлять типы, которые изначально не могут быть распознаны. Для этого есть виртуальная функция bool CustomType(string str, StringData *info), которая вызывается каждый раз, когда класс не может распознать тип. Она должна возвратить true, если ей удалось узнать тип, и false в противном случае. Т.о. для того, чтобы добавить новый тип, надо:
В производном классе переопределить функцию CustomType
Сделать потомка от StringData, в который добавить новое поле и флаг, соответствующие новому типу
Вот пример того, как я это сделал для распознавания цветов, которые записаны в виде ARGB (например, 255, 0, 157, 46):
#include "..\PARSER\TextParser.h"
#define SD_GUITEXTDATA 2
// Означает, что распознанное значение - цвет
#define ST_COLOR 4
// Класс для заполнения парсером - добавился цвет
class CGuiTextData : public StringData
{
public:
D3DCOLOR Color;
virtual int GetType() { return SD_GUITEXTDATA; };
};
class CGuiTextParser : public CTextParser
{
public:
CGuiTextParser();
virtual ~CGuiTextParser();
virtual bool CustomType(string str, StringData *info); // Будет пытаться распознать цвет
}; |