Created
December 28, 2017 03:20
-
-
Save lixingcong/7cbce61233bcae74cb6def6300a4d787 to your computer and use it in GitHub Desktop.
IniSettings: Windows-Like INI file storage
This file contains 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
/* | |
* IniSettings.cpp | |
* | |
* Created on: 2017年12月25日下午2:55:07 | |
* Author: lixingcong | |
*/ | |
//#include "StdAfx.h" | |
#include "IniSettings.h" | |
#include <iostream> | |
#include <fstream> | |
const std::string comment_prefix="# "; | |
const std::string newline="\n"; | |
CIniSettings::CIniSettings(std::string filename) | |
{ | |
this->filename=filename; | |
InitializeCriticalSection(&cs_write); | |
InitializeCriticalSection(&cs_map); | |
} | |
CIniSettings::~CIniSettings() | |
{ | |
DeleteCriticalSection(&cs_write); | |
DeleteCriticalSection(&cs_map); | |
} | |
bool CIniSettings::is_section_exist(std::string section) | |
{ | |
return map_sections.find(section)!=map_sections.end(); | |
} | |
bool CIniSettings::add_section(std::string section, std::string comment) | |
{ | |
SECTION_T s; | |
s.comment=comment; | |
std::pair<std::map<std::string,SECTION_T>::iterator, bool> ret; | |
EnterCriticalSection(&cs_map); | |
ret=map_sections.insert(PAIR_SECTION_T(section,s)); | |
LeaveCriticalSection(&cs_map); | |
return ret.second; | |
} | |
bool CIniSettings::set_section_comment(std::string section, std::string comment) | |
{ | |
if(false==is_section_exist(section)){ | |
return add_section(section,comment); | |
}else{ | |
EnterCriticalSection(&cs_map); | |
map_sections[section].comment=comment; | |
LeaveCriticalSection(&cs_map); | |
} | |
return true; | |
} | |
std::string CIniSettings::get_section_comment(std::string section) | |
{ | |
if(true==is_section_exist(section)){ | |
return map_sections[section].comment; | |
} | |
return std::string(); // NULL string | |
} | |
bool CIniSettings::set_value(std::string key, std::string value, std::string section) | |
{ | |
if(false==is_section_exist(section)){ | |
add_section(section); | |
} | |
EnterCriticalSection(&cs_map); | |
map_sections[section].map_items[key]=value; | |
LeaveCriticalSection(&cs_map); | |
return true; | |
} | |
bool CIniSettings::set_string(std::string key, std::string value, std::string section) | |
{ | |
return set_value(key,value,section); | |
} | |
std::string CIniSettings::get_string(std::string key, std::string section) | |
{ | |
return get_value(key,section); | |
} | |
std::string CIniSettings::get_value(std::string key, std::string section) | |
{ | |
if(false==is_section_exist(section)){ | |
return std::string(); // NULL string | |
} | |
std::map<std::string,std::string> items=map_sections[section].map_items; | |
if(items.find(key)==items.end()){ | |
return std::string(); // NULL string | |
} | |
return items[key]; | |
} | |
bool CIniSettings::save() | |
{ | |
EnterCriticalSection(&cs_write); | |
std::ofstream out_file (filename.c_str()); | |
if (!out_file.is_open()) | |
return false; | |
std::map<std::string,SECTION_T>::iterator it_section; | |
std::map<std::string,std::string>::iterator it_item; | |
for(it_section=map_sections.begin();it_section!=map_sections.end();/*++it_section*/){ | |
if(it_section->second.comment!=std::string()){ | |
out_file << get_full_comment(it_section->second.comment) << std::endl; | |
} | |
out_file << "[" << it_section->first << "]" << std::endl; | |
for(it_item=it_section->second.map_items.begin(); | |
it_item!=it_section->second.map_items.end(); | |
++it_item) | |
{ | |
out_file << it_item->first << "=" << it_item->second << std::endl; | |
} | |
if(++it_section!=map_sections.end()) /*++it_section*/ | |
out_file << std::endl; | |
} | |
out_file.close(); | |
LeaveCriticalSection(&cs_write); | |
return true; | |
} | |
bool CIniSettings::load() | |
{ | |
std::string s; | |
std::ifstream in_file (filename.c_str()); | |
if (!in_file.is_open()) | |
return false; | |
EnterCriticalSection(&cs_map); | |
map_sections.clear(); | |
std::string comment=std::string(); | |
std::string section; | |
std::string key,value; | |
bool is_found_comment=false; | |
while(!std::getline(in_file, s).eof()){ | |
trim(s); | |
if(s.empty()) | |
continue; | |
if(starts_with(s,comment_prefix)){ | |
s.erase(0,comment_prefix.length()); | |
if(true==is_found_comment){ | |
comment+=(s+newline); | |
}else{ | |
comment=(s+newline); | |
is_found_comment=true; | |
} | |
}else if(*s.begin()=='[' && *s.rbegin()==']'){ | |
section=s.substr(1,s.length()-2); | |
if(true==is_found_comment){ // comment is not empty | |
comment=comment.substr(0,comment.length()-newline.length()); // remove last '\n' | |
is_found_comment=false; | |
} | |
add_section(section,comment); | |
comment=std::string(); // NULL string | |
}else if(s.find('=')!=std::string::npos){ | |
key=s.substr(0,s.find('=')); | |
value=s.substr(s.find('=')+1); | |
set_value(key,value,section); | |
} | |
} | |
in_file.close(); | |
LeaveCriticalSection(&cs_map); | |
return true; | |
} | |
inline void CIniSettings::trim(std::string& s) | |
{ | |
// Trim example: https://gist.github.com/springmeyer/4236211 | |
s.erase(s.begin(), std::find_if(s.begin(), s.end(), is_not_space)); | |
s.erase(std::find_if(s.rbegin(), s.rend(), is_not_space).base(), s.end()); | |
} | |
bool CIniSettings::is_not_space(int ch) | |
{ | |
if (ch == 32 || ch == 160 || ch == '\n' || ch == '\r' || ch == '\t') | |
return false; | |
return true; | |
} | |
std::string CIniSettings::get_full_comment(std::string comment) | |
{ | |
// C++ replace example: https://stackoverflow.com/questions/4643512/replace-substring-with-another-substring-c | |
if(std::string()==comment) | |
return comment; | |
std::string comment_full=comment_prefix+comment; | |
std::string replace_by=(newline+comment_prefix); | |
int len_comment_prefix=(int)comment_prefix.length(); | |
int len_newline=(int)newline.length(); | |
size_t index = len_comment_prefix; | |
while (true) { | |
/* Locate the substring to replace. */ | |
index = comment_full.find(newline, index); | |
if (index == std::string::npos) | |
break; | |
/* Make the replacement. */ | |
comment_full.replace(index, len_newline, replace_by); | |
/* Advance index forward so the next iteration doesn't pick it up as well. */ | |
index += (len_newline+len_comment_prefix); | |
} | |
return comment_full; | |
} |
This file contains 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
/* | |
* IniSettings.h | |
* | |
* Created on: 2017年12月25日下午2:55:07 | |
* Author: lixingcong | |
*/ | |
#ifndef INISETTINGS_H_ | |
#define INISETTINGS_H_ | |
#include <string> | |
#include <map> | |
#include <vector> | |
#include <sstream> | |
#include <algorithm> // std::remove_if | |
#include <windows.h> // CRITICAL_SECTION | |
class CIniSettings | |
{ | |
protected: | |
struct SECTION_T{ | |
std::string comment; | |
std::map<std::string, std::string> map_items; | |
}; | |
typedef std::pair<std::string,SECTION_T> PAIR_SECTION_T; | |
typedef std::pair<std::string,std::string> PAIR_KEYVALUE_T; | |
public: | |
CIniSettings(std::string filename); | |
virtual ~CIniSettings(); | |
bool save(); | |
bool load(); | |
bool is_section_exist(std::string section); | |
bool add_section(std::string section, std::string comment=std::string()); | |
bool set_section_comment(std::string section, std::string comment); | |
std::string get_section_comment(std::string section); | |
bool set_string(std::string key, std::string value, std::string section); | |
std::string get_string(std::string key, std::string section); | |
protected: // members | |
std::map<std::string, SECTION_T> map_sections; | |
std::string filename; | |
CRITICAL_SECTION cs_write; | |
CRITICAL_SECTION cs_map; | |
protected: // member functions | |
bool set_value(std::string key, std::string value, std::string section); | |
std::string get_value(std::string key, std::string section); | |
// Trim empty space | |
static inline void trim(std::string& s); | |
static bool is_not_space(int ch); | |
std::string get_full_comment(std::string comment); | |
protected: // templates | |
template <typename T> | |
std::string get_string_from_value(T value) | |
{ | |
std::stringstream ss; | |
ss << value; | |
return ss.str(); | |
} | |
template <typename T> | |
T get_value_from_string(const std::string& str, T default_value) | |
{ | |
std::stringstream ss(str); | |
T result; | |
return ss >> result ? result: default_value; | |
} | |
template<class T> | |
bool starts_with(const T& input, const T& match) | |
{ | |
return input.size() >= match.size() && equal(match.begin(), match.end(), input.begin()); | |
} | |
public: | |
template <typename T> | |
bool set_number(std::string key, T value, std::string section) | |
{ | |
return set_value(key,get_string_from_value(value),section); | |
} | |
template <typename T> | |
T get_number(std::string key, std::string section ,T default_value) | |
{ | |
std::string str_value=get_value(key,section); | |
return (T)get_value_from_string(str_value,default_value); | |
} | |
}; | |
#endif /* INISETTINGS_H_ */ |
This file contains 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
#include "IniSettings.h" | |
#include <iostream> | |
// complie: g++ main.cpp IniSettings.cpp -o main.exe | |
int main () | |
{ | |
CIniSettings ini("test.ini"); | |
int apple=100; | |
double banana=200.999; | |
std::string cat="Moew"; | |
std::cout<<"begin: apple="<<apple<<std::endl; | |
std::cout<<"begin: banana="<<banana<<std::endl; | |
std::cout<<"begin: cat="<<cat<<std::endl; | |
if(false==ini.load()){ // file not exist | |
ini.add_section("section1","here is comment\nhello comment!"); | |
ini.add_section("section2","here is string part"); | |
ini.set_number("apple",apple,"section1"); | |
ini.set_number("banana",banana,"section1"); | |
ini.set_string("cat",cat,"section2"); | |
}else{ | |
apple=ini.get_number("apple","section1",(int)-1); // must declare the default return type | |
banana=ini.get_number("banana","section1",(double)-1); | |
cat=ini.get_string("cat","section2"); | |
std::cout<<"got: apple="<<apple<<std::endl; | |
std::cout<<"got: banana="<<banana<<std::endl; | |
std::cout<<"got: cat="<<cat<<std::endl; | |
apple+=1; | |
banana+=10; | |
cat+="?"; | |
} | |
ini.set_number("apple",apple,"section1"); | |
ini.set_number("banana",banana,"section1"); | |
ini.set_string("cat",cat,"section2"); | |
std::cout<<"save: apple="<<apple<<std::endl; | |
std::cout<<"save: banana="<<banana<<std::endl; | |
std::cout<<"save: cat="<<cat<<std::endl; | |
ini.save(); | |
std::cout<<"Press a key to exit"<<std::endl; | |
std::cin.ignore(); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment