Hi,
I made .cfg parser based on this:
https://github.com/SFML/SFML/wiki/Source%3A-Settings-Parser the basic diference is that mine is divided by sectors
Its not great but i would like to share
sorry if its not the right place to post it.
EDIT: now has comment support
TODO: add vector<string> support
Convert.hpp
#ifndef _CONVERT_HPP
#define _CONVERT_HPP
#include <string>
#include <sstream>
namespace Convert
{
//public:
// Convert T, which should be a primitive, to a std::string.
template <typename T>
static std::string T_to_string(T const &val)
{
std::ostringstream ostr;
ostr << val;
return ostr.str();
}
// Convert a std::string to T.
template <typename T>
static T string_to_T(std::string const &val)
{
std::istringstream istr(val);
T returnVal;
if (!(istr >> returnVal))
exit(EXIT_FAILURE);
return returnVal;
}
template <>
static std::string string_to_T(std::string const &val)
{
return val;
}
};
#endif
ConfigParser.hpp
#ifndef _CONFIGPARSER_HPP
#define _CONFIGPARSER_HPP
#include <string>
#include <map>
#include <vector>
#include "Convert.hpp"
typedef std::map< const std::string, std::pair< std::string, std::string > > DataFormat;
typedef std::map< const std::string, DataFormat > CfgFormat;
class ConfigParser
{
public:
ConfigParser(char comsep='#');
~ConfigParser();
bool Read(const std::string& filename);
bool Write(const std::string& filename);
bool SectionExists(const std::string& section) const;
// Only for primitives
template <typename TYPE>
TYPE Get(const std::string& section, const std::string& key, const TYPE& defValue=TYPE()) const
{
if(SectionExists(section))
if(mProperties.at(section).count(key))
return Convert::string_to_T<TYPE>(mProperties.at(section).at(key).first);
return defValue;
};
// Only for primitives
template <typename TYPE>
void Set(const std::string& section, const std::string& key, const TYPE value, const std::string& comment="")
{
mIsChanged = true;
if(!SectionExists(section))
{
DataFormat prop;
prop[key] = std::make_pair(Convert::T_to_string(value), comment);
mProperties[section] = prop;
mOrder.push_back(section);
}
else
{
mProperties[section][key].first = Convert::T_to_string(value);
if(comment.size())
{
mProperties[section][key].second = comment;
}
}
};
private:
CfgFormat mProperties;
std::vector<const std::string> mOrder;
bool mIsChanged;
std::string mFileName;
char mComSep;
};
#endif
ConfigParser.cpp
#include <fstream>
#include <iostream>
#include "ConfigParser.hpp"
ConfigParser::ConfigParser(char comsep):
mProperties(),
mOrder(),
mIsChanged(false),
mFileName("config.cfg"),
mComSep(comsep)
{
}
ConfigParser::~ConfigParser()
{
if(mIsChanged)
Write(mFileName);
}
bool ConfigParser::Read(const std::string& filename)
{
mFileName = filename;
std::ifstream in(filename.c_str());
if(!in.is_open())
{
std::cerr << "error at reading" << std::endl;
return false;
}
std::string line, section, value, param, comment;
while(!in.eof())
{
char temp[256];
in.getline(temp, 256);
line.assign(temp);
size_t lstart = line.find_first_not_of(' '); // remove whitespace before text
if(line.size() > 0 && lstart != line.npos)
{
if(line[lstart]=='[') // check for new section
{
size_t start = line.find_first_not_of(' ', lstart+1); // remove whitespace before text. +1 to ignore '['
size_t end = line.find_first_of(" ]", start); // get the text until a ']' or whitespace
end -= start; // from start to end
section = line.substr(start, end);
param = "";
value = "";
comment = "";
}
else if(line[lstart]==mComSep) //check for comment
{
mOrder.push_back(line.substr(lstart));
}
else // check for param and value
{
//get param
size_t end = line.find_first_of(" =", lstart);
end -= lstart;
param = line.substr(lstart, end);
// get value
size_t start = line.find_first_of("=", lstart);
start++;
start = line.find_first_not_of(' ', start);
end = line.find_first_of(mComSep, start);
end -= start;
value = line.substr(start, end);
start = line.find_first_of(mComSep, lstart);
if(start!=line.npos)
comment = line.substr(start);
else
comment = "";
}
}
if(section.size())
{
if(SectionExists(section))
{
if(param.size() && value.size())
mProperties.at(section).insert(std::make_pair(param, std::make_pair(value, comment)));
}
else // the first time it will come here
{
DataFormat prop;
mProperties.insert(std::make_pair(section, prop));
mOrder.push_back(section);
}
}
}
in.close();
return true;
}
bool ConfigParser::Write(const std::string& filename)
{
std::ofstream out(filename);
if(!out.is_open())
{
std::cerr << "error at writing" << std::endl;
return false;
}
std::string section = "";
std::vector<const std::string>::const_iterator iter;
for(iter=mOrder.begin(); iter!=mOrder.end(); ++iter)
{
if((*iter)[0] == mComSep)
out << (*iter) << std::endl;
else
{
if(section!=(*iter))
{
section = (*iter);
out << "[" << section << "]" << std::endl;
}
DataFormat::const_iterator iter2;
for(iter2=mProperties.at(*iter).begin(); iter2!=mProperties.at(*iter).end(); ++iter2)
{
if(iter2->second.second.size())
out << iter2->first << " = " << iter2->second.first << " " << iter2->second.second << std::endl;
else
out << iter2->first << " = " << iter2->second.first << std::endl;
}
out << std::endl;
}
}
out.close();
mIsChanged = false;
return true;
}
bool ConfigParser::SectionExists(const std::string& section) const
{
return mProperties.count(section) == 1;
}
main.cpp
#include <iostream>
#include "ConfigParser.hpp"
int main(void)
{
ConfigParser cp;
cp.Read("teste.cfg");
cp.Set<int>("window1", "width1", 800);
cp.Set<bool>("newSector", "newBool", true);
cp.Set<bool>("newSector", "newBool2", false);
cp.Set<int>("newSector", "newInt", 49);
cp.Set<int>("newSector", "newInt2", 1000);
cp.Set<float>("newSector2", "newFloat", 1.73);
cp.Set<float>("newSector2", "newFloat", 2.499);
cp.Set<double>("newSector2", "newDouble", 0.012);
cp.Set<std::string>("newSector2", "newString", "I'm a big string of text!!!", "#change the comment");
std::cout << cp.Get<bool>("teste", "bool") << std::endl;
std::cout << cp.Get<int>("window", "width") << std::endl;
std::cout << cp.Get<float>("newSector2", "newFloat") << std::endl;
std::cout << cp.Get<int>("Invalid", "Invalid", 27) << std::endl; // invalid does not exist so it will return 27
std::cout << cp.Get<std::string>("newSector2", "newString", "string does not exist") << std::endl;
cp.Write("teste.cfg");
system("pause");
return 0;
}
teste.cfg
#Welcome!!!
#this is a comment
#
#
[window]
bpp = 32 #blablabla
height = 200 # comment 1
vsync = 0 ##other comment
width = 100 ###### yet another comment
#this is my second window config
[window1]
width1 = 800#nospace
# sound stuff
[sound]
fx = 1.0 # max volume is 1.0
music = 0.5
[teste]
bool = 1
[teste2]
#teste2 is empty