我的编程经验之一 程序配置信息的保存
在我们编写的每一个程序中,我们都要保存一些有用的信息,如何保存这些信息呢?
比较常用的有三种方法,一种是使用注册表,一种是使用Ini文件,还有就是使用文件。
采用那种方法保存配置信息,不同的人可能有不同的习惯,这三种方法那种最适合你
的程序呢,究竟那个最方便呢?
一、使用注册表
在BCB中有个TRegistry,它提供了方便的注册表操作,它有几个重要的属性和方法。
如下:
__property HKEY RootKey = {read=FRootKey, write=SetRootKey, nodefault};
这个属性用来设置和取得当前的根键。在默认情况下它的值是HKEY_CURRENT_USER。
这个属性是我们常常用到的。
bool __fastcall OpenKey(const AnsiString Key, bool CanCreate);
这个方法用来打开一个键,bool CanCreate是表示如果这个键不存在则是否创建这个
键,true是创建。如果打开成功返回true。
void __fastcall CloseKey(void);
这个方法关闭当前打开的键,当你完成注册表的修改后,应该调用此方法来保存您的
修改。
bool __fastcall KeyExists(const AnsiString Key);
这个方法判断一个键是否存在。
bool __fastcall ValueExists(const AnsiString Name);
这个方法判断当前键下是否存在指定的数据项。
AnsiString __fastcall ReadString(const AnsiString Name);
这个方法从当前键下指定的数据项读取一个字符串。
void __fastcall WriteString(const AnsiString Name, const AnsiString Value);
这个方法向当前键下指定的数据项写入一个字符串。类似的函数还有ReadInteger,
WriteInteger,ReadBool,WriteBool等方法,这里就不一一列举了,请大家看一下在
线帮助就可以了,下面我举一个使用注册表保存和读取程序配置信息的例子来说明如何
用注册表读取和保存程序的配置信息。
下面这个例子从HKEY_LOCAL_MACHINE\\Software\\MyInfo键下读取配置信息,如果程序
是第一次运行则建立这个键。(使用TRegistry类,需要包含Registry.hpp)
#include <Registry.hpp>
void __fastcall ReadConfig ()
{
TRegistry * reg = new TRegistry();
reg->RootKey = HKEY_LOCAL_MACHINE;
if ( reg->OpenKey ( "\Software\MyInfo", true ) )
{//打开HKEY_LOCAL_MACHINE\\Software\\MyInfo键如果不存在则创建
if ( reg->ValueExists ( "DataPath" ) )
{//判断是否存在DataPath这个数据项,存在则读取
SysConfig.DataPath = reg->ReadString ( "DataPath" );
}else{//如果不存在这个数据项则采用程序默认路径
SysConfig.DataPath = DefaultPath;
}
}
reg->CloseKey();//关闭打开的键。
delete reg;
}
下面这个例子是保存信息到注册表,如果不存在这个键则创建并保存。
void __fastcall SaveConfig ()
{
TRegistry * reg = new TRegistry();
reg->RootKey = HKEY_LOCAL_MACHINE;
if ( reg->OpenKey ( "\Software\MyInfo", true ) )
{//打开HKEY_LOCAL_MACHINE\\Software\\MyInfo键如果不存在则创建
reg->WriteString ( "DataPath", SysConfig.DataPath );
}
reg->CloseKey();//关闭打开的键并保存信息
delete reg;
}
这两个例子很简单,其它的一些读写方法也和读取和写入String差不多,大家可以试试。
使用注册表要注意的问题。
使用注册表保存配置信息是多数软件所采用的方法,用注册表的优点是用户轻易不
能够取得和修改这些配置信息,能够有一定的安全性和隐蔽性,如果你采用二进制方式
写入的话,用户将很难知道你保存的数据内容,这样可以使你的数据更安全它的缺点就
是你要注意在Windows的不同版本下,注册表的结构是不同的,而且在NT和WIN2000下,
如果权限不够有些键系统是不允许你访问的。再就是用户很讨厌软件在它的注册表里写
入一些东西。这也就是流行的绿色软件之说了。
二、使用INI配置文件
在BCB中有个TIniFile类,它提供了对INI文件的操作,INI文件一种通用的配置文件格式
它也象注册表一样分键和数据项,下面是一个INI配置文件的内容,它有一个键LastConfig,
在这个键下有一个数据项DefaultDit,在数据项后面就是这个数据项的值。
[LastConfig]
DefaultDir=E:\王乐东程序\Sjgl第三版
TIniFile类有几个常用的方法给大家介绍一下:
__fastcall TIniFile(const AnsiString FileName) : Inifiles::TCustomIniFile(FileName) { }
这个方法创建一个INI对象,如果FileName指定的文件名不存在则自动生成此文件,
FileName可以用全路径,指定INI文件的地点,如果只有文件名没有路径,这个函数将到Windows
系统路径下去打开或创建。(WIN9X是Windows目录,NT和2000是WINNT目录)
bool __fastcall SectionExists(const AnsiString Section);
这个方法判断是否存在Section这个键。
virtual AnsiString __fastcall ReadString(const AnsiString Section, const AnsiString Ident, const AnsiString Default);
这个方法从INI文件中读取一个字符串,Section是指定的键,Ident是指定键下的数据项
Default是如果这个项不存在的时候的默认值。
virtual void __fastcall WriteString(const AnsiString Section, const AnsiString Ident, const AnsiString Value);
这个方法是向INI文件中写入一个字符串,Senction是指定的键,Ident是指定的数据项,
Value是要写入的数据。
同TRegistry一样,TIniFile类还提供了读取整型、布尔型,二进制等数据类型的读写方法,
大家看看联机帮助就可以了,用法基本一样。下面就那上面的INI文件为例来说明一下如何
有INI文件来保存配置信息和如何从INI文件中读取数据。
下面的例子是如何从一个INI文件中读取配置,此配置文件保存在应用程序的目录下,如果
没有配置,则取程序所在路径为默认值。
#include <inifiles.hpp>
void __fastcall ReadConfig ()
{//读取配置,
TIniFile * regKey;
AnsiString ExePath = ExtractFileName( ParamStr ( 0 ) );
regKey = new TIniFile ( ExePath + "TrimTxt.ini");
DefaultDir = regKey->ReadString ( "LastConfig", "DefaultDir" , ExePath );
delete regKey;
}
下面的例子说明如何将信息写入INI文件
void __fastcall SaveConfig ()
{
TIniFile * regKey;
AnsiString ExePath = ExtractFileName( ParamStr ( 0 ) );
regKey = new TIniFile ( ExePath + "TrimTxt.ini");
regKey->WriteString ( "LastConfig", "DefaultDir" , DefaultDir );
delete regKey;
}
使用INI文件保存配置信息是很方便的,它最大的优点就是用户可以手工去就改它,它通常
用来保存一些不是至关重要的数据。它的缺点就是不适合保存结构比较复杂的数据,而且文件
容易被破坏。
三、使用文件
使用文件来保存程序的配置,大概是现在不常用到的方法。因为对文件的操作是让很多
初学头疼的事,但通过适当的方法,用文件保存数据还是非常方便和灵活的。
在BCB中使用文件常用的有如下几种方式。
A、用fopen,fwrite比较传统,但比较灵活。
B、使用Stream,如FileStream和MemoryStream,优点是比较方便。
使用文件保存数据我常用的是通过定义一个结构来进行,操作,如以下的结构
struct {
char UserName[10];//用户名
char PassWord[16];//用户密码
int Level[24]; //用户权限
}UserInfo;
这个结构用来保存程序使用者的一些信息,(我随便定义的不一定合理,只是作个例子)
如果用注册表和INI文件来保存都很麻烦,可是如果用文件就很简单了。下面我分别用A、B两
种方式来保存和读取。
下面的这两个例子用传统的文件读写方式来读写配置文件
#include <stdio.h>
bool __fastcall ReadConfig ()
{//读取配置文件
FILE * ptr;
AnsiString FileName = ExtractFileName( ParamStr ( 0 ) ) + "UserInfo.Dat";
ptr = fopen ( FileName.c_str(), "rb" );
if ( ptr == NULL )
{
ShowMessage ( "文件打开失败!" );
return false;
}
fseek ( ptr, 0L, SEEK_END );//定位到文件尾
if ( ftell ( ptr ) != sizeof ( UserInfo ) )
{//判断文件是否被破坏。
ShowMessage ( "配置文件被破坏!" );
fclose ( ptr );
reutrn false;
}
fseek ( ptr, 0L, SEEK_SET );//定位到文件头
fread ( &UserInfo, sizeof ( UserInfo ), 1, ptr );//读取内容到结构。
fclose ( ptr );
return true;
}
void __fastcall SaveConfig()
{
FILE * ptr;
AnsiString FileName = ExtractFileName( ParamStr ( 0 ) ) + "UserInfo.Dat";
ptr = fopen ( FileName.c_str(), "wb" );
if ( ptr == NULL )
{
ShowMessage ( "文件打开失败!" );
return false;
}
fwrite ( &UserInfo, sizeof ( UserInfo ), 1, ptr );//读取内容到结构。
fclose ( ptr );
}
这样就完成了一个配置文件的读写。下面在用Stream来处理这个文件。
bool __fastcall ReadConfig ()
{//用流来读取,我用内存流是因为我的个人习惯,其实用文件流也是一样的。
TMemoryStream * readStream = new TMemoryStream ();
AnsiString FileName = ExtractFileName( ParamStr ( 0 ) ) + "UserInfo.Dat";
readStream->LoadFromFile ( FileName );
if ( readStream->Size != sizeof ( UserInfo ) )
{
ShowMessage ( "配置文件被破坏!" );
delete readStream;
reutrn false;
}
readStream->Seek ( 0, soFromBeginning );//定位到文件头
readStream->ReadBuffer ( &UserInfo, sizeof ( UserInfo ) );//读取文件内容到结构
delete readStream;
}
void __fastcall SaveConfig ()
{
TMemoryStream * readStream = new TMemoryStream ();
AnsiString FileName = ExtractFileName( ParamStr ( 0 ) ) + "UserInfo.Dat";
readStream->WriteBuffer ( &UserInfo, sizeof ( UserInfo ) );//将结构写入流
readStream->Seek ( 0, soFromBeginning );//定位到头
readStream->SaveToFile ( FileName );
delete readStream;
}
这样完成了用流来读写数据,通过对这两种方法的比较,用流方便一些。
用文件来保存数据的缺点就是有些繁琐,而且文件容易被删除,但它的优点就可以保存
大量、复杂的数据,这时使用注册表和INI文件所无法完成的。
以上的三种配置文件的就我的个人观点来看,如果不是要求安全,用文件是最好的,它
最灵活,而且可以快速的读取和写入。这也是我最长使用的方法。当然了使用注册表和INI
文件也可以完成要求,一切就看你的喜好了。