//========================================================================
//TITLE:
// Wince读取ini的CIniParse类
//AUTHOR:
// norains
//DATE:
// Monday 22-December-2008
//Environment:
// WINCE5.0 + VS2005 + ARM BSP
//========================================================================
挺奇怪的是,微软在wince中没有像桌面系统般有读取ini的特定函数。为了解决该问题,网上有不少牛人都给给出了相应的解决方案,比如说:
benkaoya的直接C++/C代码读写:http://blog.csdn.net/benkaoya/archive/2008/01/28/2070648.aspx
xumercury的将以上函数封装为类:http://blog.csdn.net/xumercury/archive/2008/07/09/2629346.aspx
khan的STL读写类:http://www.cppblog.com/Khan/archive/2007/08/09/29459.html?opt=admin
不过,以上的这几个都有一些小问题,或是效率较低,或是有一些小bug,其实最引起自己重写冲动的是因为命名规则和自己的理念有冲突,所以索性就自己另起炉灶弄了个CIniParse。因为习惯问题,我的代码基本上充斥的是STL代码,如果不喜欢这风格,可以使用benkaoya或xumercury的作品,他们的这个代码也是比较成熟的;关于khan的LIniFile类,在他blog的留言中,似乎公布的这个类代码是有缺陷的,完整的代码需要发信向其索取。因为我没有他的完整版代码,所以我也不做更多的评论。
写完这段有损人利己嫌疑的言语后,我们来看看今天的主角:CIniParse。之前说了别人的代码的缺陷,现在也该说说CIniParse所存在的一些问题了。可能最让人不爽的是,CIniParse类在保存的时候,会将原来的注释全部清除掉,并且原来的顺序也全部重排。如果注释以及顺序对你非常重要,请勿使用CIniParse类。
除此以外,该类并不能在ASCII环境中编译通过。只是wince是UNICODE的系统,所以这点在平时使用中倒不会引起很大的麻烦。当然,如果你是打算移植到VC6.0中,那么可能有一些函数就必须要修正了。再一点就是,该类没有详细地进行测试,所以应该还会有或多或少的问题,如果你发现了,也希望我更正,期待你的指出。
惯例,先罗列出CIniParse的完整代码:
头文件:
- //Class Name
- // CIniParse
- //
- //Version:
- // 1.0.0
- //
- //Date:
- // 2008.12.22
- //
- //Author:
- // norains
- //
- #pragma once
- #include "windows.h"
- #include <string>
- #include <vector>
- #include <map>
- #ifdef UNICODE
- #ifndef TSTRING
- #define TSTRING std::wstring
- #endif
- #else
- #ifndef TSTRING
- #define TSTRING std::string
- #endif
- #endif
- class CIniParse
- {
- public:
- //-------------------------------------------------------------------------------
- //Description:
- // Open the ini file.
- //-------------------------------------------------------------------------------
- BOOL Open(TSTRING strFile);
- //-------------------------------------------------------------------------------
- //Description:
- // Get the profile value as string type
- //-------------------------------------------------------------------------------
- TSTRING GetPrivateProfileString(const TSTRING &strSection,const TSTRING &strKey);
- //-------------------------------------------------------------------------------
- //Description:
- // Get the profile value as int type
- //-------------------------------------------------------------------------------
- int GetPrivateProfileInt(const TSTRING &strSection,const TSTRING &strKey);
- //-------------------------------------------------------------------------------
- //Description:
- // Set the profile value as string type.The function wouldn't save the data to file
- //but memory. If you want to save to the file, you must call Flush function
- //-------------------------------------------------------------------------------
- BOOL SetPrivateProfileString(const TSTRING &strSection,const TSTRING &strKey,TSTRING strSet);
- //-------------------------------------------------------------------------------
- //Description:
- // Set the profile value as int type.The function wouldn't save the data to file
- //but memory. If you want to save to the file, you must call Flush function
- //-------------------------------------------------------------------------------
- BOOL SetPrivateProfileInt(const TSTRING &strSection,const TSTRING &strKey,int iSet);
- //-------------------------------------------------------------------------------
- //Description:
- // Flush the memory buffer to the file
- //-------------------------------------------------------------------------------
- BOOL Flush();
- public:
- CIniParse();
- virtual ~CIniParse();
- private:
- //-------------------------------------------------------------------------------
- //Description:
- // Get the line value base on the current offset. After calling, the offset value
- //would move to the head of next line
- //-------------------------------------------------------------------------------
- TSTRING GetLine();
- //-------------------------------------------------------------------------------
- //Description:
- // Check the string value of one line whether is comment or not
- //-------------------------------------------------------------------------------
- static BOOL IsCommentLine(const TSTRING & strLine);
- //-------------------------------------------------------------------------------
- //Description:
- // Check the string value of one line whether is the section.
- //Parameters:
- // strLine : [in] The string value buffer.
- //-------------------------------------------------------------------------------
- static BOOL IsSectionLine(const TSTRING & strLine);
- //-------------------------------------------------------------------------------
- //Description:
- // Get the key value from one line
- //Parameters:
- // strLine : [in] The buffer to find
- //-------------------------------------------------------------------------------
- static TSTRING GetKeyValueFromLine(const TSTRING & strLine);
- //-------------------------------------------------------------------------------
- //Description:
- // Get the key name from one line
- //Parameters:
- // strLine : [in] The buffer to find
- //-------------------------------------------------------------------------------
- static TSTRING GetKeyNameFromLine(const TSTRING & strLine);
- //-------------------------------------------------------------------------------
- //Description:
- // Get the section name from one line
- //Parameters:
- // strLine : [in] The buffer to find
- //-------------------------------------------------------------------------------
- static TSTRING GetSectionNameFromLine(const TSTRING & strLine);
- //-------------------------------------------------------------------------------
- //Description:
- // Remove the space from the string
- //Parameters:
- // strBuf : [in] The buffer to remove
- //Return Value:
- // Return the string without space
- //-------------------------------------------------------------------------------
- static TSTRING RemoveSpace(const TSTRING &strBuf);
- //-------------------------------------------------------------------------------
- //Description:
- // Parse the ini file
- //-------------------------------------------------------------------------------
- void Parse(const TSTRING &strBuf);
- //-------------------------------------------------------------------------------
- //Description:
- // Convert the string to lowercase
- //-------------------------------------------------------------------------------
- static TSTRING ConvertToLowercase(const TSTRING &strBuf);
- //-------------------------------------------------------------------------------
- //Description:
- // Reset the offset point to the begin
- //-------------------------------------------------------------------------------
- BOOL ResetOffset();
- //-------------------------------------------------------------------------------
- //Description:
- // Check whether the offset arrived the end of the buffer.
- //-------------------------------------------------------------------------------
- BOOL IsOffsetEnd();
- private:
- TSTRING m_strFileBuf;
- TSTRING::size_type m_stOffset;
- TSTRING m_strFilePath;
- std::map<TSTRING, std::map<TSTRING,TSTRING> > m_mpValue; //The first TSTRING is section name, the second is key name and last is the value.
- };
实现体:
- #include "IniParse.h"
- #include <deque>
- #include <algorithm>
- CIniParse::CIniParse():
- m_stOffset(0)
- {}
- CIniParse::~CIniParse()
- {}
- BOOL CIniParse::Open(TSTRING strFile)
- {
- HANDLE hFile = CreateFile(strFile.c_str(),GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
- if(hFile == INVALID_HANDLE_VALUE)
- {
- return FALSE;
- }
- m_strFilePath = strFile;
- DWORD dwSize = GetFileSize(hFile,NULL);
- //Read the file to buffer
- std::vector<BYTE> vtBuf(dwSize,0);
- DWORD dwRead = 0;
- ReadFile(hFile,&vtBuf[0],vtBuf.size(),&dwRead,NULL);
- vtBuf.resize(dwRead);
- CloseHandle(hFile);
- #ifdef UNICODE
- m_strFileBuf.clear();
- if(vtBuf.size() >= 2)
- {
- if(vtBuf[0] == 0xFF && vtBuf[1] == 0xFE)
- {
- //Unicode file
- //Convert the read buffer to the unicode.The TCHAR here is equal to wchar_t
- std::deque<TCHAR> dqBuf;
- for(std::vector<BYTE>::size_type i = 2; i < vtBuf.size(); i += 2)
- {
- dqBuf.push_back(vtBuf[i] + (vtBuf[i+1] << 8));
- }
- m_strFileBuf.insert(m_strFileBuf.end(),dqBuf.begin(),dqBuf.end());
- }
- else
- {
- std::vector<TCHAR> vtBufT;
- vtBufT.resize(MultiByteToWideChar (CP_ACP, 0, reinterpret_cast<char *>(&vtBuf[0]), vtBuf.size(), NULL, 0));
- MultiByteToWideChar (CP_ACP, 0, reinterpret_cast<char *>(&vtBuf[0]), vtBuf.size(), &vtBufT[0], vtBufT.size());
- m_strFileBuf.insert(m_strFileBuf.end(),vtBufT.begin(),vtBufT.end());
- }
- }
- #else
- #error "Unfortunately! The source code donesn't complete in the ASCII environment"
- #endif
- Parse(m_strFileBuf);
- return TRUE;
- }
- TSTRING CIniParse::GetLine()
- {
- TSTRING strReturn;
- if(m_stOffset >= m_strFileBuf.size())
- {
- return TSTRING();
- }
- TSTRING::size_type stPos = m_strFileBuf.find(TEXT("/r/n"),m_stOffset);
- if(stPos == TSTRING::npos)
- {
- strReturn.insert(0,m_strFileBuf,m_stOffset,m_strFileBuf.size() - m_stOffset + 1);
- //Move the offset position to end of the file
- m_stOffset = m_strFileBuf.size() + 1;
- }
- else
- {
- strReturn.insert(0,m_strFileBuf,m_stOffset,stPos - m_stOffset);
- //Move the offset position to the back of the "/r/n"
- m_stOffset = stPos + 2;
- }
- return strReturn;
- }
- BOOL CIniParse::IsCommentLine(const TSTRING &strLine)
- {
- if(strLine.empty() != FALSE || strLine[0] == ';')
- {
- return TRUE;
- }
- else
- {
- return FALSE;
- }
- }
- BOOL CIniParse::IsSectionLine(const TSTRING &strLine)
- {
- TSTRING::size_type stLeft = strLine.find(TEXT("["));
- TSTRING::size_type stRight = strLine.find(TEXT("]"));
- if(strLine.empty() == FALSE && stLeft != TSTRING::npos && stRight != TSTRING::npos && stRight > stLeft)
- {
- return TRUE;
- }
- else
- {
- return FALSE;
- }
- }
- TSTRING CIniParse::GetKeyValueFromLine(const TSTRING &strLine)
- {
- TSTRING::size_type stPosEqual = strLine.find(TEXT("="));
- if(stPosEqual == TSTRING::npos)
- {
- return TSTRING();
- }
- TSTRING strReturn;
- TSTRING::size_type stPosQuoteLeft = strLine.find(TEXT("/""),stPosEqual);
- if(stPosQuoteLeft != TSTRING::npos)
- {
- TSTRING::size_type stPosQuoteRight = strLine.find(TEXT("/""),stPosQuoteLeft + 1);
- if(stPosQuoteRight != TSTRING::npos && stPosQuoteLeft + 1 != stPosQuoteRight)
- {
- strReturn.insert(0,strLine,stPosQuoteLeft + 1,stPosQuoteRight - stPosQuoteLeft - 1);
- return strReturn;
- }
- }
- //Store the string to the buffer
- if(stPosEqual + 1 > strLine.size())
- {
- return TSTRING();
- }
- TSTRING strBuf(strLine,stPosEqual + 1,strLine.size() - stPosEqual);;
- return RemoveSpace(strBuf);
- }
- TSTRING CIniParse::GetKeyNameFromLine(const TSTRING &strLine)
- {
- TSTRING::size_type stPosEqual = strLine.find(TEXT("="));
- if(stPosEqual == 0)
- {
- return TSTRING();
- }
- TSTRING strBuf;
- if(stPosEqual == TSTRING::npos)
- {
- //All of the string line is the key name
- strBuf = strLine;
- }
- else
- {
- strBuf.clear();
- strBuf.insert(0,strLine,0,stPosEqual);
- }
- return RemoveSpace(strBuf);
- }
- TSTRING CIniParse::GetSectionNameFromLine(const TSTRING &strLine)
- {
- TSTRING::size_type stLeft = strLine.find(TEXT("["));
- TSTRING::size_type stRight = strLine.find(TEXT("]"));
- if(!(strLine.empty() == FALSE && stLeft != TSTRING::npos && stRight != TSTRING::npos && stRight > stLeft))
- {
- return TSTRING();
- }
- TSTRING strBuf(strLine,stLeft + 1,stRight - stLeft - 1);
- return RemoveSpace(strBuf);
- }
- TSTRING CIniParse::RemoveSpace(const TSTRING &strBuf)
- {
- if(strBuf.find(TEXT(" ")) != TSTRING::npos)
- {
- //Remove the space
- TSTRING strReturn;
- for(TSTRING::size_type stPos = 0; stPos < strBuf.size(); stPos ++)
- {
- if(strBuf[stPos] != ' ')
- {
- strReturn.push_back(strBuf[stPos]);
- }
- }
- return strReturn;
- }
- else
- {
- //No space
- return strBuf;
- }
- }
- void CIniParse::Parse(const TSTRING &strBuf)
- {
- //Reset the file pointer to the begin
- ResetOffset();
- std::map<TSTRING,TSTRING> mpKey;
- TSTRING strSection;
- while(TRUE)
- {
- TSTRING strLine = GetLine();
- if(strLine.empty() != FALSE || IsCommentLine(strLine) != FALSE)
- {
- if(IsOffsetEnd())
- {
- break;
- }
- else
- {
- continue;
- }
- }
- if(IsSectionLine(strLine) != FALSE)
- {
- if(strSection.empty() != FALSE)
- {
- //It's the first section
- strSection = GetSectionNameFromLine(strLine);
- continue;
- }
- //Store the last section value
- m_mpValue.insert(std::make_pair(strSection,mpKey));
- strSection = GetSectionNameFromLine(strLine);
- mpKey.clear();
- }
- else
- {
- if(strSection.empty() != FALSE)
- {
- //The section name is empty, so needn't store the key value
- continue;
- }
- //Store the key value and name
- TSTRING strKeyName = GetKeyNameFromLine(strLine);
- if(strKeyName.empty() == FALSE)
- {
- mpKey.insert(std::make_pair(strKeyName,GetKeyValueFromLine(strLine)));
- }
- }
- }
- //Store the last section value
- if(strSection.empty() == FALSE)
- {
- m_mpValue.insert(std::make_pair(strSection,mpKey));
- }
- }
- TSTRING CIniParse::ConvertToLowercase(const TSTRING &strBuf)
- {
- std::vector<TCHAR> vtBuf(strBuf.length() + 1,0);
- _tcscpy(&vtBuf[0],strBuf.c_str());
- return _tcslwr(&vtBuf[0]);
- }
- BOOL CIniParse::ResetOffset()
- {
- m_stOffset = 0;
- return TRUE;
- }
- BOOL CIniParse::IsOffsetEnd()
- {
- if(m_stOffset >= m_strFileBuf.size())
- {
- return TRUE;
- }
- else
- {
- return FALSE;
- }
- }
- TSTRING CIniParse::GetPrivateProfileString(const TSTRING &strSection,const TSTRING &strKey)
- {
- if(m_mpValue.empty() != FALSE)
- {
- return TSTRING();
- }
- //Ignoring the character case to find the specified key
- for(std::map<TSTRING, std::map<TSTRING,TSTRING>>::iterator iterSection = m_mpValue.begin(); iterSection != m_mpValue.end(); iterSection ++)
- {
- if(ConvertToLowercase(iterSection->first) == ConvertToLowercase(strSection))
- {
- for(std::map<TSTRING,TSTRING>::iterator iterKey = iterSection->second.begin(); iterKey != iterSection->second.end(); iterKey ++)
- {
- if(ConvertToLowercase(iterKey->first) == ConvertToLowercase(strKey))
- {
- return iterKey->second;
- }
- }
- }
- }
- return TSTRING();
- }
- int CIniParse::GetPrivateProfileInt(const TSTRING &strSection,const TSTRING &strKey)
- {
- return _ttoi(GetPrivateProfileString(strSection,strKey).c_str());
- }
- BOOL CIniParse::SetPrivateProfileString(const TSTRING &strSection,const TSTRING &strKey,TSTRING strSet)
- {
- //Ignoring the character case to find the specified key
- for(std::map<TSTRING, std::map<TSTRING,TSTRING>>::iterator iterSection = m_mpValue.begin(); iterSection != m_mpValue.end(); iterSection ++)
- {
- if(ConvertToLowercase(iterSection->first) == ConvertToLowercase(strSection))
- {
- for(std::map<TSTRING,TSTRING>::iterator iterKey = iterSection->second.begin(); iterKey != iterSection->second.end(); iterKey ++)
- {
- if(ConvertToLowercase(iterKey->first) == ConvertToLowercase(strKey))
- {
- iterKey->second = strSet;
- return TRUE;
- }
- }
- //Add the new key value
- iterSection->second.insert(std::make_pair(strKey,strSet));
- return TRUE;
- }
- }
- //Add the new section and key value
- std::map<TSTRING,TSTRING> mpKey;
- mpKey.insert(std::make_pair(strKey,strSet));
- m_mpValue.insert(std::make_pair(strSection,mpKey));
- return TRUE;
- }
- BOOL CIniParse::SetPrivateProfileInt(const TSTRING &strSection,const TSTRING &strKey,int iSet)
- {
- std::vector<TCHAR> vtBuf(MAX_PATH,0);
- TSTRING strSet = _itot(iSet,&vtBuf[0],10);
- return SetPrivateProfileString(strSection,strKey,strSet);
- }
- BOOL CIniParse::Flush()
- {
- TSTRING strWrite;
- //strWrite.reserve(m_mpValue.size());
- //Store the string value to the buffer
- for(std::map<TSTRING, std::map<TSTRING,TSTRING>>::iterator iterSection = m_mpValue.begin(); iterSection != m_mpValue.end(); iterSection ++)
- {
- strWrite += TEXT("/r/n[");
- strWrite += iterSection->first;
- strWrite += TEXT("]/r/n");
- for(std::map<TSTRING,TSTRING>::iterator iterKey = iterSection->second.begin(); iterKey != iterSection->second.end(); iterKey ++)
- {
- strWrite += iterKey->first;
- strWrite += TEXT("=");
- strWrite += iterKey->second;
- strWrite += TEXT("/r/n");
- }
- }
- //Write to the file
- HANDLE hFile = CreateFile(m_strFilePath.c_str(),GENERIC_WRITE,NULL,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
- if(INVALID_HANDLE_VALUE != hFile)
- {
- std::vector<BYTE> vtWrite;
- vtWrite.reserve(sizeof(TCHAR) * strWrite.size());
- #ifdef UNICODE
- vtWrite.push_back(0xFF);
- vtWrite.push_back(0xFE);
- for(TSTRING::iterator iter = strWrite.begin(); iter != strWrite.end(); iter++)
- {
- vtWrite.push_back(static_cast<BYTE>(*iter));
- vtWrite.push_back((*iter)>>8);
- }
- #else
- vtWrite.assign(strWrite.begin(),strWrite.end());
- #endif
- DWORD dwWrite = 0;
- WriteFile(hFile,&vtWrite[0],vtWrite.size(),&dwWrite,NULL);
- CloseHandle(hFile);
- return TRUE;
- }
- else
- {
- return FALSE;
- }
- }
具体使用如下:
- int WINAPI WinMain( HINSTANCE hInstance,
- HINSTANCE hPrevInstance,
- LPTSTR lpCmdLine,
- int nCmdShow)
- {
- //声明一个对象
- CIniParse iniParse;
- //打开相应的ini文件
- iniParse.Open(TEXT("//NAND//test.ini"));
- TSTRING strValue ;
- int iValue = 0;
- //获取特定的SECTION和KEY的数值。可以有两种返回形式,一种是TSTRING,另一种是int。
- strValue = iniParse.GetPrivateProfileString(TEXT("VERSION_INI_FILE"),TEXT("VERSION_CONFIG_INFO"));
- iValue = iniParse.GetPrivateProfileInt(TEXT("VERSION_INI_FILE"),TEXT("VERSION_CONFIG_INFO"));
- //更改相应KEY的数值
- iniParse.SetPrivateProfileString(TEXT("VERSION_INI_FILE"),TEXT("VERSION_CONFIG_INFO"),TEXT("5600"));
- strValue = iniParse.GetPrivateProfileString(TEXT("VERSION_INI_FILE"),TEXT("VERSION_CONFIG_INFO"));
- //增加新的SECTION和KEY数值
- iniParse.SetPrivateProfileString(TEXT("VERSION_INI_FILE_NEW"),TEXT("VERSION_CONFIG_INFO_NEW"),TEXT("98600"));
- strValue = iniParse.GetPrivateProfileString(TEXT("VERSION_INI_FILE_NEW"),TEXT("VERSION_CONFIG_INFO_NEW"));
- //写到文件中
- iniParse.Flush();
- return 0;
- }
在这里还有一点需要注意的是,因为从效率考虑,SetPrivateProfileString函数更改的数值都只是在内存中做修改,如果需要保存到文件中,需要调用Flush函数。调用Flush函数后,内存的数据就保存到之前Open传入的文件路径中。
如果想保存到C++动态数组中,也可以实现,只是有点麻烦:
- strValue = iniParse.GetPrivateProfileString(TEXT("VERSION_INI_FILE_NEW"),TEXT("VERSION_CONFIG_INFO_NEW"));
- TCHAR *pNewBuf = new TCHAR[strValue.size() + 1];
- _tcscpy(pNewBuf,strValue.c_str());
- delete []pNewBuf;
用动态数组还需要手动释放资源,相对来说,就不如直接用STL来得更为简便。