Skip to content

Instantly share code, notes, and snippets.

@lixingcong
Created December 28, 2017 03:20
Show Gist options
  • Save lixingcong/7cbce61233bcae74cb6def6300a4d787 to your computer and use it in GitHub Desktop.
Save lixingcong/7cbce61233bcae74cb6def6300a4d787 to your computer and use it in GitHub Desktop.
IniSettings: Windows-Like INI file storage
/*
* 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;
}
/*
* 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_ */
#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