Last active
June 27, 2018 11:17
-
-
Save grisu48/e65ccaa8cd163e7c6e1bf603ee5a350c to your computer and use it in GitHub Desktop.
Standalone config file reader
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * 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