有没有遇到这种情况:1.文件是一个ini格式,但是不想存成文件。2.在Linux(或其他非windows平台)遇到ini文件需要读取。怎么办?
经过十来个小时的编码,改进,编码,改进。。。。。完成了下面的代码。我是在内存中解析的ini文件,如果你不需要内存中解析,那么可以稍微修改一下。废话不多说,上代码吧。效率不是很高,改进也没太在效率上改进,但能用。
四个文件,如下:拷贝过去直接用就可以了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
#ifndef INIMEMORY_H #define INIMEMORY_H #include <string> #include <list> /********************************************************************************************************* * 程序功能:内存解析ini文件 * 作者:独行 * 时间:2017.9.5 * 目标: 1.保证正确解析ini文件,可以正常读取ini文件中指定属性的值; * 2.解析修改ini文件,保证修改后的文件可以被其他程序/windows API识别。 * 备注: 1.程序不支持多线程,多线程程序使用需要小心; * 2.向类传入的缓冲区要保证修改后空间足够使用,如果空间不足,会造成缓冲区溢出; * 3.如果文件中有注释,那么对文件执行写操作之后所有注释都会消失; * 4.程序如果进行过写入操作,那么所有段名,变量名全部都会变成小写,且所有空格 tab都会消失; * 5.程序如果进行过写入操作,文件中的\r\n一律会变成\n; * 6.程序传递std::string类型的参数,如果是空,一定不要出现NULL 而要使用"",因为传递的是引用,传递NULL会出错。 * ————————————————————————————————————————————————————————————————————————————————————————————————————————— * 更新: * 2017/9/13: * 因为发现在开机右下角程序中直接传入CStringA的buffer到本类中,CStringA对象销毁,VS会报错,所以 * 修改程序,不再将传入的Buffer作为内部Buffer了,而自己重新申请一个Buffer,自己管理配置文件的存储 * 空间将原来Buffer的内容复制过去,效率低,但是不会出错。 * 已知遗留问题: * 拷贝构造函数需要重写 * =重载需要重写 * 需要进一步测试 * ————————————————————————————————————————————————————————————————————————————————————————————————————————— * 更新: * 2017/9/13: * 重新改为使用原始内存,但是这次不同的是内部不再保存原来的内存地址,解析完直接扔掉,不知道CString * 会不会出现问题,没有测试,这样类内部不再保存Buffer,而是直接保存一个Ini格式的数据结构 *********************************************************************************************************/ class IniMemoryPrivate; class IniMemory { public: //构造析构 IniMemory(); IniMemory(char* strIniBuffer); IniMemory(IniMemory& iniMemory); ~IniMemory(); //运算符的重载 IniMemory& operator=(char*); IniMemory& operator=(const IniMemory&); /* 功能:重新设置buffer 参数: strIniBuffer 新的配置文件 返回值: 是否重新设置成功 */ bool SetBuffer(char* strIniBuffer); /* 功能:从INI文件中获取字符串数据,不需要考虑数据的长度 参数: const std::string& strAppName 段的名字 const std::string& strKeyName 属性的名字 const std::string& strDefault 默认值,如果这个属性不存在,那么返回这个值 返回值: 返回strKeyName对应的值,如果该strKeyName不存在,那么返回strDefault */ std::string GetIniString(const std::string& strAppName, const std::string& strKeyName, const std::string& strDefault); /* 功能:从INI文件中获取一个数值 参数: const std::string& strAppName 段的名字 const std::string& strKeyName 属性的名字 const int& nDefault 默认值,如果这个属性不存在,那么返回这个值 返回值: 返回strKeyName对应的值,如果该strKeyName不存在,那么返回nDefault */ int GetIniInt(const std::string& strAppName, const std::string& strKeyName, const int& nDefault); /* 功能:从INI文件中指定位置写入一个属性,如果该属性已经存在那么这个的属性为strString 参数: const std::string& strAppName 段的名字 const std::string& strKeyName 属性的名字 const std::string& strString strKeyName对应的的值 返回值: 如果成功返回true,失败返回false */ bool WriteIniString(const std::string& strAppName, const std::string& strKeyName, const std::string& strString); /* 功能:从INI文件中指定位置写入一个属性,如果该属性已经存在那么这个的属性为strString 参数: size_t nBufferSize const std::string& strAppName 段的名字 const std::string& strKeyName 属性的名字 const std::string& strString strKeyName对应的的值 返回值: 如果成功返回true,失败返回false */ bool WriteIniStringToNewBuffer(char* newBuffer, size_t nBufferSize, const std::string& strAppName, const std::string& strKeyName, const std::string& strString); /* 功能:返回INI文件中所有段的名字,名字放在strReturnedStringList中 参数: std::list& strReturnedStringList 存放所有段的名字 返回值: 如果成功返回true,失败返回false */ bool GetIniSection(std::list& strReturnedStringList); /* 功能:返回一个段中所有属性的名字,名字放在strReturnedStringList中 参数: const std::string& strAppName 这个段的名字 std::list& strReturnedStringList 存放所有属性的名字 返回值: 如果成功返回true,失败返回false */ bool GetIniSectionProperties(const std::string& strAppName, std::list& strReturnedStringList); /* 功能:通过Buffer刷新程序的内部结构,函数用在通过外部更改了缓冲区内容的情形 参数: 无 返回值: 是否刷新成功 */ bool ReProcessIni(); /*获取输出配置文件需要的缓冲区大小*/ size_t BufferSize(); /* 功能:将配置文件输出到新的Buffer 参数: char* NewBuffer 需要存放配置文件的新缓冲区 size_t 缓冲区的大小 返回值: 是否刷新成功*/ bool WriteToNewBuffer(char* NewBuffer, size_t nBufferSize); private: friend class IniMemoryPrivate; IniMemoryPrivate* my; char* strIniBuffer; private: //将格式化的数据写入strIniBuffer bool FlushBuffer(char*, size_t); }; #endif // INIMEMORY_H |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 |
#include "IniMemory.h" #include "IniMemoryPrivate.h" #include <ctype> #include <cstring> #include <new> #include <exception> IniMemory::IniMemory():my(new IniMemoryPrivate(this)) { strIniBuffer = nullptr; my->listSection.clear(); } IniMemory::IniMemory(char* strIni): my(new IniMemoryPrivate(this)), strIniBuffer(nullptr) { //将最后一个字符设置为换行 strIniBuffer = strIni; //初始化失败,直接异常中断处理 if(!ReProcessIni()){ delete my; my = nullptr; throw "初始化失败,请检查ini内容是否合法。"; } } IniMemory::IniMemory(IniMemory& iniMemory): my(new IniMemoryPrivate(this)), strIniBuffer(nullptr) { //完全拷贝 this->strIniBuffer = iniMemory.strIniBuffer; my->listSection = iniMemory.my->listSection; } IniMemory::~IniMemory() { if(my){ delete my; my = nullptr; } } IniMemory& IniMemory::operator=(char * buffer) { //如果空间不够,那么释放空间 if(nullptr == my){ my = new IniMemoryPrivate(this); } this->strIniBuffer = buffer; //初始化失败,直接异常中断处理 if(!ReProcessIni()){ if(strIniBuffer){ strIniBuffer = nullptr; } my->listSection.clear(); } return *this; } IniMemory& IniMemory::operator=(const IniMemory& iniMem) { if(&iniMem == this){ return *this; } if(nullptr == my){ my = new IniMemoryPrivate(this); } strIniBuffer = iniMem.strIniBuffer; my->listSection = iniMem.my->listSection; return *this; } bool IniMemory::SetBuffer(char *strIni) { this->strIniBuffer = strIni; if(nullptr == my){ my = new IniMemoryPrivate(this); } //初始化失败,直接异常中断处理 if(!ReProcessIni()){ if(strIniBuffer){ strIniBuffer = nullptr; } my->listSection.clear(); } return true; } std::string IniMemory::GetIniString(const std::string &strApp, const std::string &strKey, const std::string &strDefault) { if(my->listSection.empty()){ return strDefault; } //将strAppName转换成小写 std::string strAppName = strApp; std::transform(strAppName.begin(), strAppName.end(), strAppName.begin(), ::tolower); for (auto itIndex = my->listSection.begin(); itIndex != my->listSection.end(); itIndex++){ if(strAppName == itIndex->strSectionName){//找到了这个段 //继续查找属性 std::string strKeyName = strKey; std::transform(strKeyName.begin(), strKeyName.end(), strKeyName.begin(), ::tolower); for (auto itSubIndex = itIndex->listVarPair.begin(); itSubIndex != itIndex->listVarPair.end(); itSubIndex++){ //如果找到了这个属性,那么返回对应的值 if(strKeyName == itSubIndex->first){ return itSubIndex->second; } } } } //如果没有找到对应的属性,那么直接返回默认值 return strDefault; } int IniMemory::GetIniInt(const std::string &strApp, const std::string &strKey, const int &nDefault) { if(my->listSection.empty()){ return nDefault; } //将strAppName转换成小写 std::string strAppName = strApp; std::transform(strAppName.begin(), strAppName.end(), strAppName.begin(), ::tolower); for (auto itIndex = my->listSection.begin(); itIndex != my->listSection.end(); itIndex++){ if(strAppName == itIndex->strSectionName){//找到了这个段 //继续查找属性 std::string strKeyName = strKey; std::transform(strKeyName.begin(), strKeyName.end(), strKeyName.begin(), ::tolower); for (auto itSubIndex = itIndex->listVarPair.begin(); itSubIndex != itIndex->listVarPair.end(); itSubIndex++){ //如果找到了这个属性,那么返回对应的值,将这个值转换成int类型返回 if(strKeyName == itSubIndex->first){ return ::atoi(itSubIndex->second.c_str()); } } } } //如果没有找到对应的属性,那么直接返回默认值 return nDefault; } bool IniMemory::WriteIniString(const std::string &strApp, const std::string &strKey, const std::string &strString) { //将strAppName转换成小写 std::string strAppName = strApp; std::transform(strAppName.begin(), strAppName.end(), strAppName.begin(), ::tolower); for (auto itIndex = my->listSection.begin(); itIndex != my->listSection.end(); itIndex++){ if(strAppName == itIndex->strSectionName){//找到了这个段 //继续查找属性 std::string strKeyName = strKey; std::transform(strKeyName.begin(), strKeyName.end(), strKeyName.begin(), ::tolower); for (auto itSubIndex = itIndex->listVarPair.begin(); itSubIndex != itIndex->listVarPair.end(); itSubIndex++){ //如果找到了这个属性,那么返回对应的值,将这个值转换成int类型返回 if(strKeyName == itSubIndex->first){ itSubIndex->second = strString; //刷新缓冲区数据 return true; } } itIndex->listVarPair.push_back(std::make_pair(std::string(strKeyName),std::string(strString))); return true; } } //插入一个新的段 IniMemoryPrivate::stSection section; section.strSectionName = strApp; my->listSection.push_back(section); my->listSection.rbegin()->listVarPair.push_back(std::make_pair(std::string(strKey),std::string(strString))); return true; //如果没有找到对应的属性,那么直接返回默认值 } bool IniMemory::WriteIniStringToNewBuffer(char *newBuffer, size_t nBufferSize, const std::string &strApp, const std::string &strKey, const std::string &strString) { //将strAppName转换成小写 std::string strAppName = strApp; std::transform(strAppName.begin(), strAppName.end(), strAppName.begin(), ::tolower); for (auto itIndex = my->listSection.begin(); itIndex != my->listSection.end(); itIndex++){ if(strAppName == itIndex->strSectionName){//找到了这个段 //继续查找属性 std::string strKeyName = strKey; std::transform(strKeyName.begin(), strKeyName.end(), strKeyName.begin(), ::tolower); for (auto itSubIndex = itIndex->listVarPair.begin(); itSubIndex != itIndex->listVarPair.end(); itSubIndex++){ //如果找到了这个属性,那么返回对应的值,将这个值转换成int类型返回 if(strKeyName == itSubIndex->first){ //保存这个值 std::string strTmp = itSubIndex->second; //设置值 itSubIndex->second = strString; //刷新缓冲区数据 bool bRes = FlushBuffer(newBuffer, nBufferSize); //还原值 itSubIndex->second = strTmp; return bRes; } } itIndex->listVarPair.push_back(std::make_pair(std::string(strKeyName),std::string(strString))); bool bRes = FlushBuffer(newBuffer, nBufferSize); itIndex->listVarPair.pop_back(); return bRes; } } //没有找到对应的段 IniMemoryPrivate::stSection section; section.strSectionName = strApp; my->listSection.push_back(section); my->listSection.rbegin()->listVarPair.push_back(std::make_pair(std::string(strKey),std::string(strString))); bool bRes = FlushBuffer(newBuffer, nBufferSize); my->listSection.pop_back(); return bRes; } bool IniMemory::GetIniSection(std::list &strReturnedString) { if(my->listSection.empty()){ return false; } for (auto itIndex = my->listSection.begin(); itIndex != my->listSection.end(); itIndex++){ strReturnedString.push_back(itIndex->strSectionName); } return true; } bool IniMemory::GetIniSectionProperties(const std::string &strApp, std::list &strReturnedStringList) { if(my->listSection.empty()){ return false; } //查找这个块,找到了就将这个块里所有的变量全部放到这list里面 //将strAppName转换成小写 std::string strAppName = strApp; std::transform(strAppName.begin(), strAppName.end(), strAppName.begin(), ::tolower); for (auto itIndex = my->listSection.begin(); itIndex != my->listSection.end(); itIndex++){ if(strAppName == itIndex->strSectionName){//找到了这个段 for (auto itSubIndex = itIndex->listVarPair.begin(); itSubIndex != itIndex->listVarPair.end(); itSubIndex++){ //将所有属性放入list中 strReturnedStringList.push_back(itSubIndex->first); } return true; } } //没有找到这个块 return false; } //重新解析 bool IniMemory::ReProcessIni() { if(nullptr == strIniBuffer){ return false; } my->listSection.clear(); char tmpBuffer[4096]; char* pEnd; pEnd = strIniBuffer; int n = 0; while (*pEnd || 0 != n){ if('\n' == *pEnd || (0 != n && !(*pEnd))){ tmpBuffer[n] = '\0'; //获得了一行的内容,处理这一行 //判断是不是一个空行,注释行,是的话直接跳过 char* pBufferStart = tmpBuffer; char* pBufferEnd =tmpBuffer + n - 1; //处理前面的空格 while (' ' == *pBufferStart || '\t' == *pBufferStart){ pBufferStart++; } //处理末尾的空格符号 while (' ' == *pBufferEnd || '\r' == *pBufferEnd || '\t' == *pBufferEnd){ *pBufferEnd-- = '\0'; } //如果是注释行,或者空行,那么直接跳过,处理下一行 if(';' == *pBufferStart || 0 == strlen(pBufferStart)){ ; }else if('[' == *pBufferStart){ pBufferStart++; char* pos; //判断是不是段名 //回这里如果找不到配对的],那么直接返false if((pos = strchr(pBufferStart, ']'))){ char* check = pos+1; *pos-- = '\0'; //这里我再处理一下空格 while (' ' == *pBufferStart || '\t' == *pBufferStart){ pBufferStart++; } while (' ' == *pos || '\t' == *pos){ *pos-- = '\0'; } if(0 != strlen(pBufferStart)){ //将段名转换成小写 do{ *pos = tolower((int)*pos); }while (pos-- != pBufferStart); //得到了一个段名,那么申请一个新的stSection节点,将段名放入 IniMemoryPrivate::stSection section; section.strSectionName = pBufferStart; my->listSection.push_back(section); } //检查是不是合法的段名 while('\0' != *check){ if(';' == *check){ break; } if(' ' != *check && '\t' != *check){ my->listSection.clear(); strIniBuffer = nullptr; return false; } check++; } }else{ strIniBuffer = nullptr; return false; } }else{ //否则判断这一行是不是属性 char* pos = nullptr; if((pos = strchr(pBufferStart, '='))){ //如果是一个变量,那么将该变量放入最后一个段的那个节点的list对中 char* pVarEnd = pos; pVarEnd--; //这里只需要把后面的空格去掉,因为前面的空格已经在最开始清除掉了 while (' ' == *pVarEnd || '\t' == *pVarEnd){ *pVarEnd-- = '\0'; } //将变量名转换成小写 while (pVarEnd != pBufferStart){ *pVarEnd = ::tolower(*pVarEnd); pVarEnd--; } *pVarEnd = ::tolower(*pVarEnd); *pos++ = '\0'; while (' ' == *pos || '\t' == *pos){ pos++; } char* pSubPos = nullptr; //注释忽略 if((pSubPos = strchr(pos, ';'))){ *pSubPos-- = '\0'; } while (pSubPos && (' ' == *pSubPos || '\t' == *pSubPos)){ *pSubPos-- = '\0'; } //设置如果变量名不空,那么放入 if(0 != strlen(pBufferStart)){ //全局的属性 if(0 == my->listSection.size()){ IniMemoryPrivate::stSection section; section.strSectionName = ""; my->listSection.push_back(section); } //将该对值放入list中 my->listSection.rbegin()->listVarPair.push_back(std::make_pair(pBufferStart,pos)); } }else{//说明不是段名,不是属性,也不是注释,那么要他干嘛,终止解析,game over strIniBuffer = nullptr; return false; } } n = 0; if('\n' == *pEnd){ pEnd++; } continue; }else{ tmpBuffer[n] = *pEnd; } n++; pEnd++; } strIniBuffer = nullptr; return true; } size_t IniMemory::BufferSize() { //返回Buffer需要的大小 return my->CountNeedBuffer(); } //将所有内容输出到新的缓冲区中 bool IniMemory::WriteToNewBuffer(char* NewBuffer, size_t nBufferSize) { return FlushBuffer(NewBuffer, nBufferSize); } //输出到新的缓冲区 bool IniMemory::FlushBuffer(char * newBuffer, size_t nBufferSize) { if(nullptr == newBuffer){ return false; } size_t Buffersize = my->CountNeedBuffer(); if(nBufferSize < Buffersize){ return false; } char* pBufferEnd = newBuffer; //不去管内存大小够不够,这个问题留给外面去考虑,我只管复制 for (auto itIndex = my->listSection.begin(); itIndex != my->listSection.end(); itIndex++){ if(itIndex != my->listSection.begin() || "" != itIndex ->strSectionName){ *pBufferEnd++ = '['; strcpy(pBufferEnd, itIndex->strSectionName.c_str()); pBufferEnd += strlen(pBufferEnd); *pBufferEnd++ = ']'; *pBufferEnd++ = '\n'; } //添加本段中的所有属性到配置文件中 for (auto itSubIndex = itIndex->listVarPair.begin(); itSubIndex != itIndex->listVarPair.end(); itSubIndex++){ //把变量名复制到相应位置 strcpy(pBufferEnd, itSubIndex->first.c_str()); pBufferEnd += strlen(pBufferEnd); *pBufferEnd++ = '='; strcpy(pBufferEnd, itSubIndex->second.c_str()); pBufferEnd += strlen(pBufferEnd); *pBufferEnd++ = '\n'; } } *pBufferEnd = '\0'; return true; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
#ifndef INIMEMORYPRIVATE_H #define INIMEMORYPRIVATE_H //list #include <list> //make_pair #include <utility> //常用算法 #include <algorithm> //std::string #include <string> #include "IniMemory.h" class IniMemory; class IniMemoryPrivate { public: IniMemoryPrivate(IniMemory* my); private: typedef struct { std::string strSectionName; std::list> listVarPair; }stSection; friend class IniMemory; IniMemory* my; std::list listSection; size_t CountNeedBuffer(); }; #endif // INIMEMORYPRIVATE_H |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
#include "IniMemory.h" #include "IniMemoryPrivate.h" IniMemoryPrivate::IniMemoryPrivate(IniMemory * my): my(my) { } size_t IniMemoryPrivate::CountNeedBuffer() { size_t Buffersize = 0; for (auto itIndex = listSection.begin(); itIndex != listSection.end(); itIndex++){ if(itIndex != listSection.begin() || "" != itIndex->strSectionName){ Buffersize += itIndex ->strSectionName.length(); Buffersize += 3; } for (auto itSubIndex = itIndex->listVarPair.begin(); itSubIndex != itIndex->listVarPair.end(); itSubIndex++){ //把变量名复制到相应位置 Buffersize += itSubIndex->first.length(); Buffersize += itSubIndex->second.length(); Buffersize += 2; } } Buffersize++;//存放最后的‘\0’ return Buffersize; } |
程序是用C++写的,用一个私有类隐藏了类成员。这样可以只发布一个二进制和一个头文件就可以了。用到了一点C++11的特性,VC2010以下的需要改一下有一个位置模板的>>和for循环中的auto,其他的不用动。