IniFile.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589
  1. #include <assert.h>
  2. #include <fstream>
  3. #include <cstring>
  4. #include <algorithm>
  5. #include <stdio.h>
  6. #include "IniFile.h"
  7. #if _MSC_VER >=1400
  8. const std::wstring to_wcs( const std::string& src )
  9. {
  10. std::string prev = setlocale(LC_CTYPE,NULL); // 保存原来的locale
  11. setlocale( LC_CTYPE, "chs" ); // 设置当前locale为chs,这在非简体中文平台上不可缺少
  12. size_t count1 = mbstowcs(NULL, src.c_str(), 0); // 计算新字符串长度
  13. std::wstring des(count1, L' ');
  14. size_t count2 = mbstowcs(&des[0], src.c_str(), count1 ); // 转化
  15. assert(count1 == count2);
  16. setlocale( LC_CTYPE, prev.c_str()); // 恢复到原来的locale
  17. return des;
  18. }
  19. #endif
  20. IniFile::IniFile()
  21. {
  22. m_prevInsertIt = m_lines.end();
  23. }
  24. IniFile::~IniFile (void)
  25. {
  26. clear();
  27. }
  28. bool IniFile::open(const char* filename)
  29. {
  30. assert(filename);
  31. clear();
  32. #if _MSC_VER >=1400
  33. std::wstring wcsfile = to_wcs(filename);
  34. std::ifstream fin(wcsfile.c_str());
  35. #else
  36. std::ifstream fin(filename);
  37. #endif
  38. if(fin.is_open())
  39. {
  40. while(!fin.eof())
  41. {
  42. std::string tmp;
  43. getline(fin, tmp, '\n');
  44. if(fin.eof() && tmp.empty())
  45. break;
  46. // if *NIX file format, remove the CR
  47. if(tmp.length() >= 1 && tmp[tmp.length()-1] == '\r')
  48. {
  49. tmp.resize(tmp.length()-1);
  50. }
  51. parseLine(tmp);
  52. }
  53. makeIndex();
  54. return true;
  55. }
  56. return false;
  57. }
  58. bool IniFile::save(const char* filename)
  59. {
  60. assert(filename);
  61. #if _MSC_VER >=1400
  62. std::wstring wcsfile = to_wcs(filename);
  63. std::ofstream fout(wcsfile.c_str());
  64. #else
  65. std::ofstream fout(filename);
  66. #endif
  67. if(fout.is_open())
  68. {
  69. std::list<LineFrame*>::iterator it = m_lines.begin();
  70. for(; it != m_lines.end(); ++it)
  71. {
  72. LineFrame* frame = *it;
  73. switch(frame->m_type)
  74. {
  75. case TYPE_COMMENT:
  76. fout << frame->m_comment << std::endl;
  77. break;
  78. case TYPE_SECTION:
  79. fout << "[" << frame->m_section << "]" << frame->m_comment << std::endl;
  80. break;
  81. case TYPE_KEYVALUE:
  82. {
  83. if(frame->m_valueHasComment)
  84. fout << frame->m_key << "=\"" << frame->m_value << "\"" << frame->m_comment << std::endl;
  85. else
  86. fout << frame->m_key << "=" << frame->m_value << frame->m_comment << std::endl;
  87. }
  88. break;
  89. default:
  90. break;
  91. }
  92. }
  93. return true;
  94. }
  95. return false;
  96. }
  97. void IniFile::clear()
  98. {
  99. std::list<LineFrame*>::iterator it = m_lines.begin();
  100. for(; it != m_lines.end(); ++it)
  101. delete *it;
  102. m_lines.clear();
  103. m_lineindex.clear();
  104. }
  105. bool IniFile::readBool(const char* section, const char* key, bool dft)
  106. {
  107. assert(section);
  108. assert(key);
  109. LineFrame *frame = findNotEmptyFrame(section, key);
  110. if(frame)
  111. {
  112. if(ignoreCaseCompare(frame->m_value,"true"))
  113. return true;
  114. else if(ignoreCaseCompare(frame->m_value,"false"))
  115. return false;
  116. else
  117. return dft;
  118. }
  119. return dft;
  120. }
  121. int IniFile::readInt(const char* section, const char* key, int dft)
  122. {
  123. assert(section);
  124. assert(key);
  125. LineFrame *frame = findNotEmptyFrame(section, key);
  126. if(frame)
  127. {
  128. if ( frame->m_value.length() > 2 && frame->m_value[0] == '0' && frame->m_value[1] == 'x' )
  129. {
  130. int value = dft;
  131. sscanf(frame->m_value.c_str(), "0x%x", &value );
  132. return value;
  133. }
  134. else if ( frame->m_value.length() > 2 && frame->m_value[0] == '0' && frame->m_value[1] == 'X' )
  135. {
  136. int value = dft;
  137. sscanf(frame->m_value.c_str(), "0X%x", &value );
  138. return value;
  139. }
  140. else
  141. {
  142. int value = dft;
  143. sscanf(frame->m_value.c_str(), "%u", &value );
  144. return value;
  145. }
  146. }
  147. else
  148. {
  149. return dft;
  150. }
  151. }
  152. double IniFile::readDouble(const char* section, const char* key, double dft)
  153. {
  154. assert(section);
  155. assert(key);
  156. LineFrame *frame = findNotEmptyFrame(section, key);
  157. if(frame)
  158. {
  159. return atof(frame->m_value.c_str());
  160. }
  161. else
  162. {
  163. return dft;
  164. }
  165. }
  166. const char* IniFile::readString(const char* section, const char* key, const char* dft)
  167. {
  168. assert(section);
  169. assert(key);
  170. LineFrame *frame = findFrame(section, key);
  171. if(frame)
  172. {
  173. return frame->m_value.c_str();
  174. }
  175. else
  176. {
  177. return dft;
  178. }
  179. }
  180. void IniFile::writeBool(const char* section, const char* key, bool val)
  181. {
  182. assert(section);
  183. assert(key);
  184. if(val)
  185. writeString(section, key, "true");
  186. else
  187. writeString(section, key, "false");
  188. }
  189. void IniFile::writeInt(const char* section, const char* key, int val)
  190. {
  191. assert(section);
  192. assert(key);
  193. char tmp[32];
  194. sprintf(tmp, "%d", val);
  195. writeString(section, key, tmp);
  196. }
  197. void IniFile::writeHex(const char* section, const char* key, uint32_t val)
  198. {
  199. assert(section);
  200. assert(key);
  201. char tmp[32];
  202. sprintf(tmp, "0x%X", val);
  203. writeString(section, key, tmp);
  204. }
  205. void IniFile::writeDouble(const char* section, const char* key, double val)
  206. {
  207. assert(section);
  208. assert(key);
  209. char tmp[32];
  210. sprintf(tmp, "%g", val);
  211. writeString(section, key, tmp);
  212. }
  213. void IniFile::writeString(const char* section, const char* key, const char* val)
  214. {
  215. assert(section);
  216. assert(key);
  217. assert(val);
  218. LineFrame *frame = findFrame(section, key);
  219. if(frame)
  220. {
  221. // already exist, update it!
  222. frame->m_value = val;
  223. frame->m_valueHasComment = hasComment(val);
  224. }
  225. else
  226. {
  227. std::list<LineFrame*>::iterator it;
  228. // try cache, if failed then locate the end of this section
  229. if(m_prevSection == section && m_prevInsertIt != m_lines.end())
  230. it = m_prevInsertIt;
  231. else
  232. it = findSectionEnd(section);
  233. // new key insert into near by section, it will speed up all write method;
  234. // std::list<LineFrame*>::iterator it = findSection(section);
  235. if(it != m_lines.end())
  236. {
  237. frame = new LineFrame;
  238. frame->m_type = TYPE_KEYVALUE;
  239. frame->m_key = key;
  240. frame->m_value = val;
  241. frame->m_valueHasComment = hasComment(val);
  242. m_lines.insert(++it, frame);
  243. m_prevSection = section;
  244. m_prevInsertIt = --it;
  245. }
  246. else
  247. {
  248. // no section yet! create it!
  249. frame = new LineFrame;
  250. frame->m_type = TYPE_SECTION;
  251. frame->m_section = section;
  252. m_lines.push_back(frame);
  253. frame = new LineFrame;
  254. frame->m_type = TYPE_KEYVALUE;
  255. frame->m_key = key;
  256. frame->m_value = val;
  257. frame->m_valueHasComment = hasComment(val);
  258. m_lines.push_back(frame);
  259. }
  260. // update index
  261. m_lineindex[section][key] = frame;
  262. }
  263. }
  264. bool IniFile::hasSection(const char* section)
  265. {
  266. return m_lineindex.find(section) != m_lineindex.end();
  267. }
  268. bool IniFile::deleteSection(const char* section)
  269. {
  270. m_lineindex.erase(section);
  271. std::list<LineFrame*>::iterator it = findSection(section);
  272. if(it != m_lines.end())
  273. {
  274. // delete the section at first
  275. delete *it;
  276. m_lines.erase(it++);
  277. // and delete all key-value until occurs next section(or end of file)
  278. while(it != m_lines.end() && (*it)->m_type != TYPE_SECTION)
  279. {
  280. delete *it;
  281. m_lines.erase(it++);
  282. }
  283. return true;
  284. }
  285. return false;
  286. }
  287. bool IniFile::deleteKey(const char* section, const char* key)
  288. {
  289. assert(section);
  290. assert(key);
  291. std::list<LineFrame*>::iterator it = findSection(section);
  292. if (it == m_lines.end())
  293. {
  294. return false;
  295. }
  296. // find from next line
  297. ++it;
  298. for( ; it != m_lines.end(); ++it)
  299. {
  300. if((*it)->m_type == TYPE_SECTION)
  301. { // occur next section
  302. break;
  303. }
  304. if((*it)->m_type == TYPE_KEYVALUE &&
  305. ignoreCaseCompare((*it)->m_key,key))
  306. {
  307. m_lineindex[section].erase(key);
  308. delete *it;
  309. m_lines.erase(it);
  310. return true;
  311. }
  312. }
  313. return false;
  314. }
  315. void IniFile::readSection(const char* section, std::map<std::string, std::string>& kvset)
  316. {
  317. assert(section);
  318. std::list<LineFrame*>::iterator it = findSection(section);
  319. if(it != m_lines.end())
  320. ++it;
  321. for( ; it != m_lines.end(); ++it)
  322. {
  323. if((*it)->m_type == TYPE_SECTION)
  324. { // occur next section
  325. break;
  326. }
  327. if((*it)->m_type == TYPE_KEYVALUE)
  328. {
  329. kvset.insert(std::make_pair((*it)->m_key, (*it)->m_value));
  330. }
  331. }
  332. }
  333. void IniFile::parseLine(const std::string& line)
  334. {
  335. LineFrame* frame = new LineFrame;
  336. m_lines.push_back(frame);
  337. frame->m_type = TYPE_COMMENT; // dft is comment
  338. // 1. check full commented line
  339. if(isCommentLine(line))
  340. {
  341. frame->m_type = TYPE_COMMENT;
  342. frame->m_comment = line;
  343. return ;
  344. }
  345. // 2. try the find section
  346. std::string::size_type first = line.find('[');
  347. std::string::size_type last = line.rfind(']');;
  348. if(first != std::string::npos && last != std::string::npos && first != last+1)
  349. {
  350. frame->m_section = line.substr(first+1,last-first-1);
  351. if(hasComment(frame->m_section))
  352. {
  353. frame->m_type = TYPE_COMMENT;
  354. frame->m_comment = line;
  355. }
  356. else
  357. {
  358. frame->m_type = TYPE_SECTION;
  359. // remember the possible comments
  360. frame->m_comment = line.substr(last+1);
  361. }
  362. return ;
  363. }
  364. // 3. find key-value pattern
  365. first = line.find('=');
  366. if(first == std::string::npos)
  367. return ;
  368. std::string strtmp1 = line.substr(0, first);
  369. if(hasComment(strtmp1))
  370. {
  371. // such case
  372. // something # key=value
  373. // we consider such meaningless line as comment
  374. frame->m_type = TYPE_COMMENT;
  375. frame->m_comment = line;
  376. return ;
  377. }
  378. std::string key = constTrim(strtmp1);
  379. if(key.empty())
  380. {
  381. // such case
  382. // = value
  383. frame->m_type = TYPE_COMMENT;
  384. frame->m_comment = line;
  385. return ;
  386. }
  387. frame->m_type = TYPE_KEYVALUE;
  388. // remember the original form
  389. frame->m_key = key;
  390. std::string strtmp2 = line.substr(first+1);
  391. std::string::size_type comment = findComment(strtmp2);
  392. std::string value = constTrim(strtmp2.substr(0, comment));
  393. if(value.size()>1 && value[0] == '\"' && value[value.size()-1] == '\"')
  394. {
  395. frame->m_value = value.substr(1, value.size()-2);
  396. frame->m_valueHasComment = true;
  397. }
  398. else
  399. {
  400. frame->m_value = value;
  401. }
  402. if(comment != std::string::npos)
  403. frame->m_comment = constTrim(strtmp2.substr(comment));
  404. return ;
  405. }
  406. std::string::size_type IniFile::findComment(const std::string& line)
  407. {
  408. bool inString = false;
  409. for(size_t i=0; i<line.size(); i++)
  410. {
  411. if(line[i] == '\"')
  412. {
  413. inString = true;
  414. }
  415. if(inString)
  416. continue;
  417. if(line[i] == '/')
  418. {
  419. // "//"
  420. if(i+1 != line.length() && line[i+1] == '/')
  421. return i;
  422. }
  423. else if(line[i] == '#' || line[i] == ';')
  424. {
  425. return i;
  426. }
  427. }
  428. return std::string::npos;
  429. }
  430. bool IniFile::isCommentLine(const std::string& line)
  431. {
  432. // skip the space and tab chars
  433. std::string::size_type charBegin = line.find_first_not_of("\t ");
  434. return charBegin != std::string::npos && charBegin == findComment(line);
  435. }
  436. bool IniFile::hasComment(const std::string& line)
  437. {
  438. return findComment(line) != std::string::npos;
  439. }
  440. std::list<LineFrame*>::iterator IniFile::findSection(const char* section)
  441. {
  442. std::list<LineFrame*>::iterator it = m_lines.begin();
  443. for( ; it != m_lines.end(); ++it)
  444. {
  445. LineFrame* frame = *it;
  446. if(frame->m_type == TYPE_SECTION
  447. && ignoreCaseCompare(frame->m_section,section))
  448. {
  449. break;
  450. }
  451. }
  452. return it;
  453. }
  454. std::list<LineFrame*>::iterator IniFile::findSectionEnd(const char* section)
  455. {
  456. std::list<LineFrame*>::iterator it = findSection(section);
  457. if(it != m_lines.end())
  458. {
  459. // find from next line
  460. ++it;
  461. while(it != m_lines.end() && (*it)->m_type != TYPE_SECTION)
  462. {
  463. ++it;
  464. }
  465. // the end of section is one step back of next section line(or end())
  466. --it;
  467. }
  468. return it;
  469. }
  470. LineFrame* IniFile::findFrame(const char*section, const char* key)
  471. {
  472. KeyValueIndex::iterator it = m_lineindex.find(section);
  473. if(it != m_lineindex.end())
  474. {
  475. KeyValueMap::iterator it2 = it->second.find(key);
  476. if(it2 != it->second.end())
  477. return it2->second;
  478. }
  479. return 0;
  480. }
  481. LineFrame* IniFile::findNotEmptyFrame(const char*section, const char* key)
  482. {
  483. LineFrame* frame = findFrame(section, key);
  484. if(frame && !frame->m_value.empty())
  485. return frame;
  486. else
  487. return 0;
  488. }
  489. void IniFile::makeIndex()
  490. {
  491. std::list<LineFrame*>::iterator it = m_lines.begin();
  492. LineFrame* frame = NULL;
  493. KeyValueIndex::iterator kvmit = m_lineindex.end();
  494. while(it != m_lines.end())
  495. {
  496. frame = *it;
  497. ++it;
  498. // 找到了一个SECTION的开始
  499. if(frame->m_type == TYPE_SECTION)
  500. {
  501. kvmit = (m_lineindex.insert(std::make_pair(frame->m_section, KeyValueMap()))).first;
  502. }
  503. else if(frame->m_type == TYPE_KEYVALUE && kvmit != m_lineindex.end())
  504. {
  505. (kvmit->second)[frame->m_key]= frame;
  506. }
  507. }
  508. }
  509. #ifdef __TEST
  510. int main()
  511. {
  512. IniFile ini;
  513. ini.open("config.ini");
  514. std::string host = ini.readString("db","host","");
  515. std::string url = ini.readString("serversetting","web1","");
  516. if(!host.empty())
  517. printf("%s\n",host.c_str());
  518. printf("%s\n",url.c_str());
  519. ini.writeInt("dbdb","timeout",123);
  520. ini.writeInt("db","a",123123);
  521. ini.writeInt("db01","a",123);
  522. ini.writeString("xxxx","name","lemon");
  523. printf("tol:%d\n",ini.readInt("serversetting","timeout",0));
  524. //clear all
  525. //ini.clear();
  526. ini.save("config.ini");
  527. }
  528. #endif