Skip to content

Instantly share code, notes, and snippets.

@grisu48
Last active June 27, 2018 11:17
Show Gist options
  • Select an option

  • Save grisu48/e65ccaa8cd163e7c6e1bf603ee5a350c to your computer and use it in GitHub Desktop.

Select an option

Save grisu48/e65ccaa8cd163e7c6e1bf603ee5a350c to your computer and use it in GitHub Desktop.
Standalone config file reader
/*
* Configuration file reader
* 2018, Felix Niederwanger
*/
#include <string>
#include <fstream>
#include <map>
#include <algorithm>
#include <cctype>
#include <locale>
/** Configuration file reader */
class ConfigRead {
private:
std::string s_filename;
int i_state = 0; // 0 = not read, 1 = ok, neg value = error
/** This contains the sections and the corresponding values */
std::map<std::string, std::map<std::string, std::string> > data;
// trim from start (in place)
void ltrim(std::string &s) {
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) {
return !std::isspace(ch);
}));
}
// trim from end (in place)
void rtrim(std::string &s) {
s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) {
return !std::isspace(ch);
}).base(), s.end());
}
// trim from both ends (in place)
void trim(std::string &s) {
ltrim(s);
rtrim(s);
}
protected:
void read(const char* filename);
public:
/** Read the given filename */
ConfigRead(const char* filename);
ConfigRead(const ConfigRead &src);
virtual ~ConfigRead() {}
/** Return true if the file is read and valid */
bool valid(void) const { return i_state == 1; }
/** Read out the given section. Returns a default (empty) map for nonexisting sections */
std::map<std::string, std::string> operator[](const char* section) {
return this->data[section];
}
bool exists(const char* section, const char* name) {
std::string value = this->data[section][name];
return value.size() > 0;
}
};
ConfigRead::ConfigRead(const char* filename) {
this->read(filename);
}
void ConfigRead::read(const char* filename) {
std::ifstream f_in(filename);
if (!f_in.is_open()) {
// XXX Error handling
return;
}
std::string line;
std::string topic = ""; // Current section name
std::map<std::string, std::string> section; // Current section data
while(getline(f_in, line)) {
trim(line);
const size_t len = line.size();
if(len <= 1) continue;
const char b = line.at(0); // Begin
const char e = line.at(len-1); // End
// Comments
if(b == '#' || b == ';' || b == ':' || b == '-') continue;
if(b == '[') {
this->data[topic] = section;
section.clear();
if (e == ']') {
topic = line.substr(1, len-2);
} else {
topic = line.substr(1); // Take the full line and hope for the best
i_state = -1; // Error in file
}
continue;
} else {
size_t i = line.find('=');
if(i == std::string::npos) {
i_state = -1;
continue;
} else {
std::string name = line.substr(0, i-1);
std::string value = line.substr(i+1);
trim(name);
trim(value);
if(name.size() > 0)
section[name] = value;
}
}
}
this->data[topic] = section;
f_in.close();
}
ConfigRead::ConfigRead(const ConfigRead &src) {
this->s_filename = src.s_filename;
this->data = src.data;
}
#if 0
// Example
#include <iostream>
using namespace std;
int main() {
// Write file
{
ofstream f_out("config.tmp");
f_out << "test = 1" << endl;
f_out << "test2 = 2" << endl;
f_out << "[section]" << endl;
f_out << "name = section" << endl;
f_out << "value = 3.14" << endl;
}
ConfigRead cf("config.tmp");
cout << "test = " << cf[""]["test"] << endl; // "" is the default section
cout << "test2 = " << cf[""]["test2"] << endl;
cout << "test3 = " << cf[""]["test3"] << endl;
cout << "test4 = " << cf[""]["test4"] << endl;
cout << "[section]" << endl;
cout << "name = " << cf["section"]["name"] << endl;
cout << "value = " << cf["section"]["value"] << endl;
cout << "test = " << cf["section"]["test"] << endl;
return 0;
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment