123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589 |
- #include <assert.h>
- #include <fstream>
- #include <cstring>
- #include <algorithm>
- #include <stdio.h>
- #include "IniFile.h"
- #if _MSC_VER >=1400
- const std::wstring to_wcs( const std::string& src )
- {
- std::string prev = setlocale(LC_CTYPE,NULL); // 保存原来的locale
- setlocale( LC_CTYPE, "chs" ); // 设置当前locale为chs,这在非简体中文平台上不可缺少
- size_t count1 = mbstowcs(NULL, src.c_str(), 0); // 计算新字符串长度
- std::wstring des(count1, L' ');
- size_t count2 = mbstowcs(&des[0], src.c_str(), count1 ); // 转化
- assert(count1 == count2);
- setlocale( LC_CTYPE, prev.c_str()); // 恢复到原来的locale
- return des;
- }
- #endif
- IniFile::IniFile()
- {
- m_prevInsertIt = m_lines.end();
- }
- IniFile::~IniFile (void)
- {
- clear();
- }
- bool IniFile::open(const char* filename)
- {
- assert(filename);
- clear();
- #if _MSC_VER >=1400
- std::wstring wcsfile = to_wcs(filename);
- std::ifstream fin(wcsfile.c_str());
- #else
- std::ifstream fin(filename);
- #endif
- if(fin.is_open())
- {
- while(!fin.eof())
- {
- std::string tmp;
- getline(fin, tmp, '\n');
-
- if(fin.eof() && tmp.empty())
- break;
- // if *NIX file format, remove the CR
- if(tmp.length() >= 1 && tmp[tmp.length()-1] == '\r')
- {
- tmp.resize(tmp.length()-1);
- }
- parseLine(tmp);
- }
- makeIndex();
- return true;
- }
- return false;
- }
- bool IniFile::save(const char* filename)
- {
- assert(filename);
- #if _MSC_VER >=1400
- std::wstring wcsfile = to_wcs(filename);
- std::ofstream fout(wcsfile.c_str());
- #else
- std::ofstream fout(filename);
- #endif
- if(fout.is_open())
- {
- std::list<LineFrame*>::iterator it = m_lines.begin();
- for(; it != m_lines.end(); ++it)
- {
- LineFrame* frame = *it;
- switch(frame->m_type)
- {
- case TYPE_COMMENT:
- fout << frame->m_comment << std::endl;
- break;
- case TYPE_SECTION:
- fout << "[" << frame->m_section << "]" << frame->m_comment << std::endl;
- break;
- case TYPE_KEYVALUE:
- {
- if(frame->m_valueHasComment)
- fout << frame->m_key << "=\"" << frame->m_value << "\"" << frame->m_comment << std::endl;
- else
- fout << frame->m_key << "=" << frame->m_value << frame->m_comment << std::endl;
-
- }
- break;
- default:
- break;
- }
- }
- return true;
- }
- return false;
- }
- void IniFile::clear()
- {
- std::list<LineFrame*>::iterator it = m_lines.begin();
- for(; it != m_lines.end(); ++it)
- delete *it;
- m_lines.clear();
- m_lineindex.clear();
- }
- bool IniFile::readBool(const char* section, const char* key, bool dft)
- {
- assert(section);
- assert(key);
- LineFrame *frame = findNotEmptyFrame(section, key);
- if(frame)
- {
- if(ignoreCaseCompare(frame->m_value,"true"))
- return true;
- else if(ignoreCaseCompare(frame->m_value,"false"))
- return false;
- else
- return dft;
- }
- return dft;
- }
- int IniFile::readInt(const char* section, const char* key, int dft)
- {
- assert(section);
- assert(key);
- LineFrame *frame = findNotEmptyFrame(section, key);
- if(frame)
- {
- if ( frame->m_value.length() > 2 && frame->m_value[0] == '0' && frame->m_value[1] == 'x' )
- {
- int value = dft;
- sscanf(frame->m_value.c_str(), "0x%x", &value );
- return value;
- }
- else if ( frame->m_value.length() > 2 && frame->m_value[0] == '0' && frame->m_value[1] == 'X' )
- {
- int value = dft;
- sscanf(frame->m_value.c_str(), "0X%x", &value );
- return value;
- }
- else
- {
- int value = dft;
- sscanf(frame->m_value.c_str(), "%u", &value );
- return value;
- }
- }
- else
- {
- return dft;
- }
- }
- double IniFile::readDouble(const char* section, const char* key, double dft)
- {
- assert(section);
- assert(key);
- LineFrame *frame = findNotEmptyFrame(section, key);
- if(frame)
- {
- return atof(frame->m_value.c_str());
- }
- else
- {
- return dft;
- }
- }
- const char* IniFile::readString(const char* section, const char* key, const char* dft)
- {
- assert(section);
- assert(key);
- LineFrame *frame = findFrame(section, key);
- if(frame)
- {
- return frame->m_value.c_str();
- }
- else
- {
- return dft;
- }
- }
- void IniFile::writeBool(const char* section, const char* key, bool val)
- {
- assert(section);
- assert(key);
- if(val)
- writeString(section, key, "true");
- else
- writeString(section, key, "false");
- }
- void IniFile::writeInt(const char* section, const char* key, int val)
- {
- assert(section);
- assert(key);
- char tmp[32];
- sprintf(tmp, "%d", val);
- writeString(section, key, tmp);
- }
- void IniFile::writeHex(const char* section, const char* key, uint32_t val)
- {
- assert(section);
- assert(key);
- char tmp[32];
- sprintf(tmp, "0x%X", val);
- writeString(section, key, tmp);
- }
- void IniFile::writeDouble(const char* section, const char* key, double val)
- {
- assert(section);
- assert(key);
- char tmp[32];
- sprintf(tmp, "%g", val);
- writeString(section, key, tmp);
- }
- void IniFile::writeString(const char* section, const char* key, const char* val)
- {
- assert(section);
- assert(key);
- assert(val);
-
- LineFrame *frame = findFrame(section, key);
- if(frame)
- {
- // already exist, update it!
- frame->m_value = val;
- frame->m_valueHasComment = hasComment(val);
- }
- else
- {
- std::list<LineFrame*>::iterator it;
- // try cache, if failed then locate the end of this section
- if(m_prevSection == section && m_prevInsertIt != m_lines.end())
- it = m_prevInsertIt;
- else
- it = findSectionEnd(section);
-
- // new key insert into near by section, it will speed up all write method;
- // std::list<LineFrame*>::iterator it = findSection(section);
- if(it != m_lines.end())
- {
- frame = new LineFrame;
- frame->m_type = TYPE_KEYVALUE;
- frame->m_key = key;
- frame->m_value = val;
- frame->m_valueHasComment = hasComment(val);
- m_lines.insert(++it, frame);
- m_prevSection = section;
- m_prevInsertIt = --it;
- }
- else
- {
- // no section yet! create it!
- frame = new LineFrame;
- frame->m_type = TYPE_SECTION;
- frame->m_section = section;
- m_lines.push_back(frame);
- frame = new LineFrame;
- frame->m_type = TYPE_KEYVALUE;
- frame->m_key = key;
- frame->m_value = val;
- frame->m_valueHasComment = hasComment(val);
- m_lines.push_back(frame);
- }
- // update index
- m_lineindex[section][key] = frame;
- }
- }
- bool IniFile::hasSection(const char* section)
- {
- return m_lineindex.find(section) != m_lineindex.end();
- }
- bool IniFile::deleteSection(const char* section)
- {
- m_lineindex.erase(section);
- std::list<LineFrame*>::iterator it = findSection(section);
- if(it != m_lines.end())
- {
- // delete the section at first
- delete *it;
- m_lines.erase(it++);
- // and delete all key-value until occurs next section(or end of file)
- while(it != m_lines.end() && (*it)->m_type != TYPE_SECTION)
- {
- delete *it;
- m_lines.erase(it++);
- }
- return true;
- }
- return false;
- }
- bool IniFile::deleteKey(const char* section, const char* key)
- {
- assert(section);
- assert(key);
- std::list<LineFrame*>::iterator it = findSection(section);
- if (it == m_lines.end())
- {
- return false;
- }
- // find from next line
- ++it;
- for( ; it != m_lines.end(); ++it)
- {
- if((*it)->m_type == TYPE_SECTION)
- { // occur next section
- break;
- }
- if((*it)->m_type == TYPE_KEYVALUE &&
- ignoreCaseCompare((*it)->m_key,key))
- {
- m_lineindex[section].erase(key);
- delete *it;
- m_lines.erase(it);
- return true;
- }
- }
- return false;
- }
- void IniFile::readSection(const char* section, std::map<std::string, std::string>& kvset)
- {
- assert(section);
- std::list<LineFrame*>::iterator it = findSection(section);
- if(it != m_lines.end())
- ++it;
- for( ; it != m_lines.end(); ++it)
- {
- if((*it)->m_type == TYPE_SECTION)
- { // occur next section
- break;
- }
- if((*it)->m_type == TYPE_KEYVALUE)
- {
- kvset.insert(std::make_pair((*it)->m_key, (*it)->m_value));
- }
- }
- }
- void IniFile::parseLine(const std::string& line)
- {
- LineFrame* frame = new LineFrame;
- m_lines.push_back(frame);
- frame->m_type = TYPE_COMMENT; // dft is comment
- // 1. check full commented line
- if(isCommentLine(line))
- {
- frame->m_type = TYPE_COMMENT;
- frame->m_comment = line;
- return ;
- }
- // 2. try the find section
- std::string::size_type first = line.find('[');
- std::string::size_type last = line.rfind(']');;
- if(first != std::string::npos && last != std::string::npos && first != last+1)
- {
- frame->m_section = line.substr(first+1,last-first-1);
- if(hasComment(frame->m_section))
- {
- frame->m_type = TYPE_COMMENT;
- frame->m_comment = line;
- }
- else
- {
- frame->m_type = TYPE_SECTION;
- // remember the possible comments
- frame->m_comment = line.substr(last+1);
- }
- return ;
- }
- // 3. find key-value pattern
- first = line.find('=');
- if(first == std::string::npos)
- return ;
- std::string strtmp1 = line.substr(0, first);
- if(hasComment(strtmp1))
- {
- // such case
- // something # key=value
- // we consider such meaningless line as comment
- frame->m_type = TYPE_COMMENT;
- frame->m_comment = line;
- return ;
- }
- std::string key = constTrim(strtmp1);
- if(key.empty())
- {
- // such case
- // = value
- frame->m_type = TYPE_COMMENT;
- frame->m_comment = line;
- return ;
- }
- frame->m_type = TYPE_KEYVALUE;
- // remember the original form
- frame->m_key = key;
- std::string strtmp2 = line.substr(first+1);
- std::string::size_type comment = findComment(strtmp2);
- std::string value = constTrim(strtmp2.substr(0, comment));
-
- if(value.size()>1 && value[0] == '\"' && value[value.size()-1] == '\"')
- {
- frame->m_value = value.substr(1, value.size()-2);
- frame->m_valueHasComment = true;
- }
- else
- {
- frame->m_value = value;
- }
- if(comment != std::string::npos)
- frame->m_comment = constTrim(strtmp2.substr(comment));
- return ;
- }
- std::string::size_type IniFile::findComment(const std::string& line)
- {
- bool inString = false;
- for(size_t i=0; i<line.size(); i++)
- {
- if(line[i] == '\"')
- {
- inString = true;
- }
-
- if(inString)
- continue;
- if(line[i] == '/')
- {
- // "//"
- if(i+1 != line.length() && line[i+1] == '/')
- return i;
- }
- else if(line[i] == '#' || line[i] == ';')
- {
- return i;
- }
- }
- return std::string::npos;
- }
- bool IniFile::isCommentLine(const std::string& line)
- {
- // skip the space and tab chars
- std::string::size_type charBegin = line.find_first_not_of("\t ");
- return charBegin != std::string::npos && charBegin == findComment(line);
- }
- bool IniFile::hasComment(const std::string& line)
- {
- return findComment(line) != std::string::npos;
- }
- std::list<LineFrame*>::iterator IniFile::findSection(const char* section)
- {
- std::list<LineFrame*>::iterator it = m_lines.begin();
- for( ; it != m_lines.end(); ++it)
- {
- LineFrame* frame = *it;
- if(frame->m_type == TYPE_SECTION
- && ignoreCaseCompare(frame->m_section,section))
- {
- break;
- }
- }
- return it;
- }
-
- std::list<LineFrame*>::iterator IniFile::findSectionEnd(const char* section)
- {
- std::list<LineFrame*>::iterator it = findSection(section);
- if(it != m_lines.end())
- {
- // find from next line
- ++it;
- while(it != m_lines.end() && (*it)->m_type != TYPE_SECTION)
- {
- ++it;
- }
- // the end of section is one step back of next section line(or end())
- --it;
- }
- return it;
- }
- LineFrame* IniFile::findFrame(const char*section, const char* key)
- {
- KeyValueIndex::iterator it = m_lineindex.find(section);
- if(it != m_lineindex.end())
- {
- KeyValueMap::iterator it2 = it->second.find(key);
- if(it2 != it->second.end())
- return it2->second;
- }
- return 0;
- }
- LineFrame* IniFile::findNotEmptyFrame(const char*section, const char* key)
- {
- LineFrame* frame = findFrame(section, key);
- if(frame && !frame->m_value.empty())
- return frame;
- else
- return 0;
- }
- void IniFile::makeIndex()
- {
- std::list<LineFrame*>::iterator it = m_lines.begin();
- LineFrame* frame = NULL;
- KeyValueIndex::iterator kvmit = m_lineindex.end();
- while(it != m_lines.end())
- {
- frame = *it;
- ++it;
- // 找到了一个SECTION的开始
- if(frame->m_type == TYPE_SECTION)
- {
- kvmit = (m_lineindex.insert(std::make_pair(frame->m_section, KeyValueMap()))).first;
- }
- else if(frame->m_type == TYPE_KEYVALUE && kvmit != m_lineindex.end())
- {
- (kvmit->second)[frame->m_key]= frame;
- }
- }
- }
- #ifdef __TEST
- int main()
- {
- IniFile ini;
- ini.open("config.ini");
- std::string host = ini.readString("db","host","");
- std::string url = ini.readString("serversetting","web1","");
- if(!host.empty())
- printf("%s\n",host.c_str());
- printf("%s\n",url.c_str());
- ini.writeInt("dbdb","timeout",123);
- ini.writeInt("db","a",123123);
- ini.writeInt("db01","a",123);
- ini.writeString("xxxx","name","lemon");
- printf("tol:%d\n",ini.readInt("serversetting","timeout",0));
- //clear all
- //ini.clear();
- ini.save("config.ini");
- }
- #endif
|