Created
December 5, 2018 14:58
-
-
Save prodeveloper0/d09a35e3dea7a82faf340f637a34dd6e to your computer and use it in GitHub Desktop.
Simple INI Parser for C++11
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
| #pragma once | |
| ////////////////////////////// | |
| // Simple INI Parser | |
| // Written by Samuel Lee(pr0ximo, prodeveloper0) | |
| // | |
| // | |
| // Usage: | |
| // sample.ini | |
| // [Pri] | |
| // ; This is my information | |
| // name = "Samuel Lee" | |
| // age = 25 | |
| // | |
| // [Sub] | |
| // ; This is my status | |
| // healthy = 20 | |
| // tired = 1000 | |
| // vision_left = 0.3 | |
| // vision_right = 0.4 | |
| // | |
| // | |
| // main.cpp | |
| // Config cfg("sample.ini"); | |
| // // No errors! | |
| // std::string name = cfg["Pri"]["name"]; | |
| // int heathy = cfg["Sub"]["healthy"]; | |
| // float vision_left = cfg["Sub"]["vision_left"]; | |
| // | |
| // // Exceptions are throwed! | |
| // int name_invalid_type = cfg["Pri"]["name"]; | |
| // | |
| #include <string> | |
| #include <map> | |
| #include <sstream> | |
| #include <fstream> | |
| #include <algorithm> | |
| template<typename T = void> | |
| class Config_ | |
| { | |
| protected: | |
| static const char* ws; | |
| static const char* ws_section; | |
| static const char* ws_property; | |
| inline std::string& rtrim(std::string& s, const char* t = ws) | |
| { | |
| s.erase(s.find_last_not_of(t) + 1); | |
| return s; | |
| } | |
| inline std::string& ltrim(std::string& s, const char* t = ws) | |
| { | |
| s.erase(0, s.find_first_not_of(t)); | |
| return s; | |
| } | |
| inline std::string& trim(std::string& s, const char* t = ws) | |
| { | |
| return ltrim(rtrim(s, t), t); | |
| } | |
| public: | |
| class Value | |
| { | |
| protected: | |
| template<typename T> | |
| static T lexical_cast(const std::string& s) | |
| { | |
| std::stringstream ss(s); | |
| T result; | |
| if((ss >> result).fail() || !(ss >> std::ws).eof()) | |
| throw std::bad_cast(); | |
| return result; | |
| } | |
| public: | |
| template<typename T> | |
| operator T () | |
| { | |
| return lexical_cast<T>(value); | |
| } | |
| public: | |
| std::string value; | |
| public: | |
| Value(const std::string& v = "") | |
| : value(v) | |
| { | |
| } | |
| public: | |
| Value& operator=(const std::string& v) | |
| { | |
| value = v; | |
| return *this; | |
| } | |
| Value& operator=(const Value& v) | |
| { | |
| value = v.value; | |
| return *this; | |
| } | |
| }; | |
| class Property | |
| { | |
| public: | |
| std::map<std::string, Value> properties; | |
| public: | |
| void Add(const std::string& k, const Value& v) | |
| { | |
| properties[k] = v; | |
| } | |
| public: | |
| Value& operator [] (const std::string& k) | |
| { | |
| auto& v = properties.find(k); | |
| if(v == properties.cend()) | |
| throw std::out_of_range("Cannot found property"); | |
| return v->second; | |
| } | |
| }; | |
| protected: | |
| std::map<std::string, Property> _Config; | |
| public: | |
| Config_(const std::string& filename = "") | |
| { | |
| if(filename.size() == 0) | |
| return; | |
| Read(filename); | |
| } | |
| public: | |
| bool Read(const std::string& filename) | |
| { | |
| std::ifstream ifs(filename); | |
| bool result = false; | |
| std::string section; | |
| std::string line; | |
| std::string key; | |
| std::string value; | |
| std::map<std::string, Property> config; | |
| // Is a file is opened successfully? | |
| if(!ifs.is_open()) | |
| goto result; | |
| while(std::getline(ifs, line)) | |
| { | |
| line = trim(line); | |
| // Is the current line a comment or blank line? | |
| if(line.size() == 0 || *line.cbegin() == ';') | |
| continue; | |
| // Is the current line a section contains brackets '[]'? | |
| if(*line.cbegin() == '[' || *line.cend() == ']') | |
| { | |
| section = trim(line, ws_section); | |
| if(section.size() == 0) | |
| goto result; | |
| continue; | |
| } | |
| // Is the current line a property? | |
| if(section.size() == 0) | |
| goto result; | |
| size_t kv_offset = line.find('='); | |
| if(kv_offset == std::string::npos) | |
| goto result; | |
| key = trim(line.substr(0, kv_offset), ws_property); | |
| value = trim(line.substr(kv_offset + 1), ws_property); | |
| if(key.size() == 0) | |
| goto result; | |
| config[section].Add(key, value); | |
| } | |
| result = true; | |
| _Config = config; | |
| result: | |
| ifs.close(); | |
| return result; | |
| } | |
| public: | |
| Property& operator [] (const std::string& s) | |
| { | |
| auto& p = _Config.find(s); | |
| if(p == _Config.cend()) | |
| throw std::out_of_range("Cannot found section"); | |
| return p->second; | |
| } | |
| }; | |
| const char* Config_<>::ws = " \t\n\r\f\v"; | |
| const char* Config_<>::ws_section = " \t\n\r\f\v[]"; | |
| const char* Config_<>::ws_property = " \t\n\r\f\v\'\""; | |
| class Config : public Config_<> | |
| { | |
| public: | |
| Config(const std::string filename = "") | |
| : Config_(filename) | |
| { | |
| } | |
| }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment