一个手工读写INI文件的类Windows中有GetPrivateProfileString 和WritePrivateProfileString函数可以进行读写INI 配置文件,但这两个函数每取出一个数据,都要打开文件,在文件中进行搜索,这样处理的效率肯定会很慢,因此下面提供了一个将配置文件读入内存中的做法,这样做的好处是一次读取文件,快速搜索(使用Map映射)。
可以将所有数据全部保存成字符串或者文件。
INI配置文件主要由四部分组成:组、键值、内容、注释和空行,下面给出一个例子文件进行说明文件:E:\boot.ini[boot loader] ;这里是一个组,下面的所有配置数据隶属于该组timeout=1 ;这里在等于好前面的是一个键值,等号后面的是一个内容default=multi(0)disk(0)rdisk(0)partition(2)\WINNT;下面一行是一个空行[operating systems];所有在';'后面的字符都属于注释,本程序不支持REM形式的注释multi(0)disk(0)rdisk(0)partition(2)\WINNT="Microsoft Windows 2000 Professional" /fastdetect;sadfkl;C:\="Microsoft Windows"好了,知道了INI文件的结构,开始分析INI文件读入内存后应使用的数据结构。
一个INI文件可以看作是由一些组以及每个组下面的数据组成的,组是字符串形式的,而数据是一个比较复杂的对象。
为了搜索的方便,所以这里采用了CMapStringToPtr来组织整个INI文件,这样的话可以由组的字符串方便地查询到该组中的数据一个组下面的数据是由一些键值— 内容组成的映射关系,所以使用CMapStringToString 来组这这些数据是最好不过的选择了。
下面给出这个类的头文件和实现部分。
给出之前简单介绍该类的用法:读取上述E:\boot.ini文件:#include "cfgdata.h"CCfgData CfgData;//Load INI文件CfgData.LoadCfgData("E:\\boot.ini");CString str;long l=0;//设置当前组CfgData.SetGroup("boot loader");//读取long型数据到变量lCfgData.GetLongData("timeout",l);//读取字符串型数据到变量strCfgData.GetStrData("default",str);//设置当前组CfgData.SetGroup("operating systems");//读取字符串型数据到变量strCfgData.GetStrData("multi(0)disk(0)rdisk(0)partition(2)\\WINNT",str);//读取字符串型数据到变量strCfgData.GetStrData("C:\\",str);//将整个配置数据保存进入字符串中CfgData.SaveToStr(&str);//将整个配置数据保存进入文件中,注意配置数据相互之间没有顺序关系,//所以可能组和组之间、一个组的几个键值--->内容配对之间的顺序将会//和以前不一致,另外所有的注释和空行丢失CfgData.SaveCfgData("E:\\boot2.ini");(读者可以点击这里获得源代码,注意解压后将boot.ini拷贝到E:\,以便程序运行找到文件)头文件CfgData.h// CfgData.h: interface for the CCfgData class.////////////////////////////////////////////////////////////////////////#if !defined(AFX_CFGDATA_H__A40CDB9A_0E44_49E6_A460_505D76BA6414__INCLUDED_) #define AFX_CFGDATA_H__A40CDB9A_0E44_49E6_A460_505D76BA6414__INCLUDED_#if _MSC_VER > 1000#pragma once#endif // _MSC_VER > 1000class CCfgData{protected://组到配置数据的映射CMapStringToPtr m_StrMapMap;//当前组CString m_strGroup;public://构造配置数据CCfgData();//析构配置数据virtual ~CCfgData();//从文件加载配置数据/*参数:LPCTSTR strFileName 加载文件的名称返回值:无*/void LoadCfgData(LPCTSTR strFileName);//将配置数据保存到文件/*参数:LPCTSTR strFileName 保存文件的名称返回值:成功返回TRUE 失败返回FALSE*/BOOL SaveCfgData(LPCTSTR strFileName);//将配置数据保存到字符串/*参数:CString* pstr 要保存的字符串指针返回值:成功返回TRUE 失败返回FALSE*/BOOL SaveToStr(CString* pstr);//设置当前读取的组/*参数:当前组名称返回值:无*/void SetGroup(LPCTSTR strGroup);//修改或者添加一条当前组中的数据/*参数:LPCTSTR strKey 要修改的数据的键值LPCTSTR strValue要修改的数据的内容返回值:备注:如果当前组在配置数据中存在,则修改或者添加该组对应键值的数据,如果当前组灾配置数据中不存在,则先创建该组*/BOOL SetData(LPCTSTR strKey,LPCTSTR strValue);//得到当前组中对应键值中字符串类型的数据/*参数:LPCTSTR strKey 要得到的数据的键值LPCTSTR strValue要得到的数据的内容返回值:找到数据返回TRUE,否则返回FALSE*/virtual BOOL GetStrData(LPCTSTR strKey,CString &strValue);//得到long型的数据/*参数:LPCTSTR strKey 要得到的数据的键值long lValue 要得到的数据的值返回值:找到数据返回TRUE,否则返回FALSE*/virtual BOOL GetLongData(LPCTSTR strKey,long &lValue);protected://释放配置数据所占用的内存/*参数:无返回值:无*/void RemoveAll();};#endif// !defined(AFX_CFGDATA_H__A40CDB9A_0E44_49E6_A460_505D76BA6414__INCLUDED_)源文件CfgData.cpp// CfgData.cpp: implementation of the CCfgData class.////////////////////////////////////////////////////////////////////////#include "stdafx.h"#include "CfgData.h"#ifdef _DEBUG#undef THIS_FILEstatic char THIS_FILE[]=__FILE__;#define new DEBUG_NEW#endif//////////////////////////////////////////////////////////////////////// Construction/Destruction////////////////////////////////////////////////////////////////////////构造配置数据CCfgData::CCfgData(){//初始化配置数据m_strGroup="设置";}//析构配置数据CCfgData::~CCfgData(){RemoveAll();}//释放配置数据所占用的内存/*参数:无返回值:无*/void CCfgData::RemoveAll(){POSITION pos=m_StrMapMap.GetStartPosition();while(pos){CMapStringToString* pStrMap;CString str;m_StrMapMap.GetNextAssoc(pos,str,(void*&)pStrMap);if(pStrMap!=NULL){pStrMap->RemoveAll();//删除掉CString--> 指针映射中的指针delete pStrMap;}}m_StrMapMap.RemoveAll();}//从文件加载配置数据/*参数:LPCTSTR strFileName 加载文件的名称返回值:无*/void CCfgData::LoadCfgData(LPCTSTR strFileName){int iReadLen=0;CString str[3];int iState=0;//0:正在读入键值1:正在读入内容2:正在读入组unsigned char ch; //正在读取的字符//清空配置数据RemoveAll();CFile file;file.Open(strFileName, CFile::modeRead);file.Seek(0,CFile::begin);str[0]="";//存放键值字符串str[1]="";//存放内容字符串str[2]="";//存放组字符串//字符串到字符串的映射,保存键值和内容CMapStringToString* pStrMap=NULL;do{iReadLen=file.Read(&ch,1);if(iReadLen!=0){//处理中文if(ch>0x80)//中文{str[iState]+=ch;iReadLen=file.Read(&ch,1);if(iReadLen!=0){str[iState]+=ch;}}else{switch(ch){//处理'['读入组字符串case'[':str[0].TrimLeft();str[0].TrimRight();str[1].TrimLeft();str[1].TrimRight();//确认键值和内容数据为空,否则可能是键值和内容中的符号if(str[0]==""&&str[1]==""){pStrMap=NULL;iState=2;str[2]="";}else{str[iState]+=ch;}break;//处理']'组字符串读入完毕case']'://确认读入的是组的字符串数据str[2].TrimLeft();str[2].TrimRight();if(iState==2&&str[2]!=""){iState=0;//新建一个组下的键值-->内容映射,放入该组pStrMap=new CMapStringToString;m_StrMapMap.SetAt(str[2],pStrMap);}else{str[iState]+=ch;}break;case'='://开始读入内容iState=1;str[1]="";break;//处理回车和注释case';':case 0x0d:case 0x0a:iState=0;//键值非空str[0].TrimLeft();str[0].TrimRight();str[1].TrimLeft();str[1].TrimRight();if(str[0]!=""&&pStrMap!=NULL){pStrMap->SetAt((LPCTSTR)str[0],(LPCTSTR)str[1]);}//处理完清空数据str[0]="";str[1]="";//注释的话继续读入直到文件结束或者碰到回车符号if(ch==';'){while((iReadLen=file.Read(&ch,1))>0){//如果遇到回车符号,终止,并且将当前位置退回if(ch==0x0d||ch==0x0a){file.Seek(-1,CFile::current);break;}}}break;default://普通字符,添加进入相应位置str[iState]+=ch;break;}}}}while(iReadLen!=0);file.Close();}//设置当前读取的组/*参数:当前组名称返回值:无*/void CCfgData::SetGroup(LPCTSTR strGroup){m_strGroup=strGroup;}//得到当前组中对应键值中字符串类型的数据/*参数:LPCTSTR strKey 要得到的数据的键值LPCTSTR strValue要得到的数据的内容返回值:找到数据返回TRUE,否则返回FALSE*/BOOL CCfgData::GetStrData(LPCTSTR strKey, CString &strValue) {CMapStringToString* pStrMap=NULL;//寻找当前组if(m_StrMapMap.Lookup(m_strGroup,(void*&)pStrMap)){if(pStrMap->Lookup(strKey,strValue))return TRUE;}return FALSE;}//得到long型的数据/*参数:LPCTSTR strKey 要得到的数据的键值long lValue 要得到的数据的值返回值:找到数据返回TRUE,否则返回FALSE*/BOOL CCfgData::GetLongData(LPCTSTR strKey, long &lValue){CString str;//得到对应的字符串数据if(CCfgData::GetStrData(strKey, str)){lValue=atoi((LPCTSTR)str);return TRUE;}return FALSE;}//修改或者添加一条当前组中的数据/*参数:LPCTSTR strKey 要修改的数据的键值LPCTSTR strValue要修改的数据的内容返回值:备注:如果当前组在配置数据中存在,则修改或者添加该组对应键值的数据,如果当前组灾配置数据中不存在,则先创建该组*/BOOL CCfgData::SetData(LPCTSTR strKey, LPCTSTR strValue){CMapStringToString* pStrMap=NULL;//如果存在该组,直接加入或者修改if(m_StrMapMap.Lookup(m_strGroup,(void*&)pStrMap)){pStrMap->SetAt(strKey,strValue);return TRUE;}else{//创建该组pStrMap=new CMapStringToString;m_StrMapMap.SetAt(m_strGroup,pStrMap);pStrMap->SetAt(strKey,strValue);}}//将配置数据保存到文件/*参数:LPCTSTR strFileName 保存文件的名称返回值:成功返回TRUE 失败返回FALSE*/BOOL CCfgData::SaveCfgData(LPCTSTR strFileName){CFile file;if(!file.Open(strFileName,CFile::modeCreate|CFile::modeWrite)) return FALSE;POSITION pos=m_StrMapMap.GetStartPosition();char ch[6]="[]\r\n=";//特殊符号CString str[3];while(pos){CMapStringToString* pStrMap;m_StrMapMap.GetNextAssoc(pos,str[2],(void*&)pStrMap);if(pStrMap!=NULL){//写入组file.Write(&ch[0],1);file.Write((LPSTR)(LPCTSTR)str[2],str[2].GetLength());file.Write(&ch[1],1);file.Write(&ch[2],2);POSITION pos1=pStrMap->GetStartPosition();while(pos1){//写入键值和内容pStrMap->GetNextAssoc(pos1,str[0],str[1]);file.Write((LPSTR)(LPCTSTR)str[0],str[0].GetLength());file.Write(&ch[4],1);file.Write((LPSTR)(LPCTSTR)str[1],str[1].GetLength());file.Write(&ch[2],2);}}}file.Close();return TRUE;}//将配置数据保存到字符串/*参数:CString* pstr 要保存的字符串指针返回值:成功返回TRUE 失败返回FALSE备注:程序流程和上面基本相同,不同的保存进入字符串中*/BOOL CCfgData::SaveToStr(CString *pstr){if(pstr==NULL)return FALSE;*pstr="";POSITION pos=m_StrMapMap.GetStartPosition();CString str[4];while(pos){CMapStringToString* pStrMap;m_StrMapMap.GetNextAssoc(pos,str[2],(void*&)pStrMap);if(pStrMap!=NULL){str[3]=*pstr;pstr->Format("%s[%s]\r\n",str[3],str[2]);POSITION pos1=pStrMap->GetStartPosition();while(pos1){pStrMap->GetNextAssoc(pos1,str[0],str[1]);str[3]=*pstr;pstr->Format("%s%s=%s\r\n",str[3],str[0],str[1]);}}}return TRUE;}。