分享
 
 
 

用Delphi编写数据报存储控件

王朝delphi·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

一、概述

在用Delphi编写数据库程序时,经常涉及到数据的导入和导出操作,如:将大型数据库中的数据存储为便携文件,以便于出外阅读;将存储在文件中的数据信息,导入到另外的数据库中;而且,通过将数据库中的数据存储为数据文件,更便于程序内部和程序间交换数据,避免通过内存交换数据的烦琐步骤,例如在笔者编写的通用报表程序中即以该控件作为数据信息传递的载体。

二、基本思路

作为数据报存储控件,应能够存储和读入数据集的基本信息(如:字段名,字段的显示名称,字段的数据类型,记录数,字段数,指定记录指定字段的当前值等),应能够提供较好的封装特性,以便于使用。

基于此,笔者利用Delphi5.0面向对象的特点,设计开发了数据报存储控件。

三、实现方法

编写如下代码单元:

unit IbDbFile;

interface

Uses Windows, SysUtils, Classes, Forms, Db, DbTables, Dialogs;

Const

Flag = '数据报-吉星软件工作室';

Type

TDsException = Class(Exception);

TIbStorage = class(TComponent)

private

FRptTitle: string; //存储数据报说明

FPageHead: string; //页头说明

FPageFoot: string; //爷脚说明

FFieldNames: TStrings; //字段名表

FStreamIndex: TStrings; //字段索引

FStream: TStream; //存储字段内容的流

FFieldCount: Integer; //字段数

FRecordCount: Integer; //记录数

FOpenFlag: Boolean; //流是否创建标志

protected

procedure Reset; //复位---清空流的内容

procedure SaveHead(ADataSet: TDataSet; Fp: TStream); //存储报表头信息

procedure LoadTableToStream(ADataSet: TDataSet); //存储记录数据

procedure IndexFields(ADataSet: TDataSet); //将数据集的字段名保存到列表中

procedure GetHead(Fp: TFileStream); //保存报表头信息

procedure GetIndex(Fp: TFileStream); //建立记录流索引

procedure GetFieldNames(Fp: TFileStream); //从流中读入字段名表

function GetFieldName(AIndex: Integer): string; //取得字段名称

function GetFieldDataType(AIndex: Integer): TFieldType;

function GetDisplayLabel(AIndex: Integer): string; //取得字段显示名称

procedure SaveFieldToStream(AStream: TStream; AField: TField); //将字段存入流中

function GetFieldValue(ARecordNo, FieldNo: Integer): string; //字段的内容

public

Constructor Create(AOwner: TComponent);

Destructor Destroy; override;

procedure Open; //创建流以准备存储数据

procedure SaveToFile(ADataSet: TDataSet; AFileName: string); //存储方法

procedure LoadFromFile(AFileName: string); //装入数据

procedure FieldStream(ARecordNo, FieldNo: Integer; var AStream: TStream);

property FieldNames[Index: Integer]: string read GetFieldName; //字段名

property FieldDataTypes[Index: Integer]: TFieldType read GetFieldDataType;

property FieldDisplayLabels[Index: Integer]: string read GetDisplayLabel;

property Fields[RecNo, FieldIndex: Integer]: string read GetFieldValue;

//property FieldStreams[RecNo, FieldIndex: Integer]: TStream read GetFieldStream;

property RecordCount: Integer read FRecordCount write FRecordCount;

property FieldCount: Integer read FFieldCount write FFieldCount;

published

property RptTitle: string read FRptTitle write FRptTitle;

property PageHead: string read FPageHead write FPageHead;

property PageFoot: string read FPageFoot write FPageFoot;

end;

function ReadAChar(AStream: TStream): Char;

function ReadAStr(AStream: TStream): string;

function ReadBStr(AStream: TStream; Size: Integer): string;

function ReadAInteger(AStream: TStream): Integer;

procedure WriteAStr(AStream: TStream; AStr: string);

procedure WriteBStr(AStream: TStream; AStr: string);

procedure WriteAInteger(AStream: TStream; AInteger: Integer);

procedure Register;

implementation

procedure Register;

begin

RegisterComponents('Data Access', [TIbStorage]);

end;

function ReadAChar(AStream: TStream): Char;

Var

AChar: Char;

begin

AStream.Read(AChar, 1);

Result := AChar;

end;

function ReadAStr(AStream: TStream): string;

var

Str: String;

C : Char;

begin

Str := '';

C := ReadAChar(AStream);

While C <> #0 do

begin

Str := Str + C;

C := ReadAChar(AStream);

end;

Result := Str;

end;

function ReadBStr(AStream: TStream; Size: Integer): string;

var

Str: String;

C : Char;

I : Integer;

begin

Str := '';

For I := 1 to Size do

begin

C := ReadAChar(AStream);

Str := Str + C;

end;

Result := Str;

end;

function ReadAInteger(AStream: TStream): Integer;

var

Str: String;

C : Char;

begin

Result := MaxInt;

Str := '';

C := ReadAChar(AStream);

While C <> #0 do

begin

Str := Str + C;

C := ReadAChar(AStream);

end;

try

Result := StrToInt(Str);

except

Application.MessageBox(' 当前字符串无法转换为整数!', '错误',

Mb_Ok + Mb_IconError);

end;

end;

procedure WriteAStr(AStream: TStream; AStr: string);

begin

AStream.Write(Pointer(AStr)^, Length(AStr) + 1);

end;

procedure WriteBStr(AStream: TStream; AStr: string);

begin

AStream.Write(Pointer(AStr)^, Length(AStr));

end;

procedure WriteAInteger(AStream: TStream; AInteger: Integer);

var

S : string;

begin

S := IntToStr(AInteger);

WriteAstr(AStream, S);

end;

Constructor TIbStorage.Create(AOwner: TComponent);

begin

inherited Create(AOwner);

FOpenFlag := False; //确定流是否创建的标志

end;

Destructor TIbStorage.Destroy;

begin

if FOpenFlag then

begin

FStream.Free;

FStreamIndex.Free;

FFieldNames.Free;

end;

inherited Destroy;

end;

procedure TIbStorage.Open;

begin

FOpenFlag := True;

FStream := TMemoryStream.Create;

FStreamIndex := TStringList.Create;

FFieldNames := TStringList.Create;

Reset;

end;

procedure TIbStorage.Reset; //复位

begin

if FOpenFlag then

begin

FFieldNames.Clear;

FStreamIndex.Clear;

FStream.Size := 0;

FRptTitle := '';

FPageHead := '';

FPageFoot := '';

FFieldCount := 0;

FRecordCount := 0;

end;

end;

//-------保存数据部分

procedure TIbStorage.SaveToFile(ADataSet: TDataSet; AFileName: string);

var

Fp: TFileStream;

I : Integer;

Ch: Char;

T1, T2: TDateTime;

Str: string;

begin

if Not FOpenFlag then

begin

showmessage(' 对象没有打开');

Exit;

end;

try

if FileExists(AFileName) then DeleteFile(AFileName);

Fp := TFileStream.Create(AFileName, fmCreate);

Reset;

SaveHead(ADataSet, Fp); //保存头部信息---附加说明

IndexFields(ADataSet); //将数据集的字段信息保存到FFieldName

LoadTableToStream(ADataSet); //保存数据集的数据信息

WriteAStr(Fp, FFieldNames.Text); //存储字段名信息

Ch := '@';

Fp.Write(Ch, 1);

WriteAStr(Fp, FStreamIndex.Text); //存储字段索引列表

Ch := '@';

Fp.Write(Ch, 1);

Fp.CopyFrom(FStream, 0);

finally

Fp.Free;

end;

end;

procedure TIbStorage.SaveHead(ADataSet: TDataSet; Fp: TStream);

Var

I : Integer;

Ch: Char;

begin

if Not ADataSet.Active then ADataSet.Active := True;

WriteAStr(Fp, Flag);

WriteAStr(Fp, FRptTitle);

WriteAStr(Fp, FPageHead);

WriteAStr(Fp, FPageFoot);

FFieldCount := ADataSet.Fields.Count;

FRecordCount := ADataSet.RecordCount;

WriteAStr(Fp, IntToStr(ADataSet.Fields.Count));

WriteAStr(Fp, IntToStr(ADataSet.RecordCount));

Ch := '@';

Fp.Write(Ch, 1);

end;

procedure TIbStorage.IndexFields(ADataSet: TDataSet);

var

I : Integer;

AField: TField;

begin

For I := 0 to ADataSet.Fields.Count - 1 do

begin

AField := ADataSet.Fields[I];

//不用FFieldNames.Values[AField.FieldName] := AField.DisplayLabel;是考虑效率

FFieldNames.Add(AField.FieldName + '=' + AField.DisplayLabel);

FFieldNames.Add(AField.FieldName + 'DataType=' + IntToStr(Ord(AField.DataType)));

end;

end;

procedure TIbStorage.LoadTableToStream(ADataSet: TDataSet);

var

No: Integer;

I, J, Size: Integer;

Tmp, Id, Str : string; //id=string(RecNO) + string(FieldNo)

Len: Integer;

Ch : Char;

BlobStream: TBlobStream;

begin

if Not FOpenFlag then

begin

showmessage(' 对象没有打开');

Exit;

end;

try

ADataSet.DisableControls;

ADataSet.First;

No := 0;

FStreamIndex.Clear;

FStream.Size := 0;

While Not ADataSet.Eof do

begin

No := No + 1;

For J := 0 to ADataSet.Fields.Count - 1 do

begin

Id := Inttostr(NO) + '_' + IntToStr(J);

//建立流的位置的索引, 索引指向: Size#0Content

FStreamIndex.Add(Id + '=' + IntToStr(FStream.Position));

//存储字段信息到流中

SaveFieldToStream(FStream, ADataSet.Fields[J]);

end;

ADataSet.Next;

end;

finally

ADataSet.EnableControls;

end;

end;

//如果一个字段的当前内容为空或者BlobSize<=0,则只写入字段大小为0, 不写入内容

procedure TIbStorage.SaveFieldToStream(AStream: TStream; AField: TField);

var

Size: Integer;

Ch: Char;

XF: TStream;

Str: string;

begin

if AField.IsBlob then

begin

//如何把一个TBlobField字段的内容存储为流

Xf := TBlobStream.Create(TBlobField(AField), bmread);

try

if Xf.Size > 0 then

begin

Size := Xf.Size;

WriteAInteger(AStream, Size);

AStream.CopyFrom(Xf, Xf.Size);

end

else

WriteAInteger(AStream, 0);

finally

XF.Free;

end;

end

else

begin

Str := AField.AsString;

Size := Length(Str);

WriteAInteger(AStream, Size);

if Size <> 0 then

AStream.Write(Pointer(Str)^, Size);

//WriteAstr(AStream, Str);

end;

Ch := '@';

AStream.Write(Ch, 1);

end;

//------------Load Data

procedure TIbStorage.LoadFromFile(AFileName: string);

var

Fp: TFileStream;

Check: string;

begin

Reset;

try

if Not FileExists(AFileName) then

begin

showmessage(' 文件不存在:' + AFileName);

Exit;

end;

Fp := TFileStream.Create(AFileName, fmOpenRead);

Check := ReadAStr(Fp);

if Check <> Flag then

begin

Application.MessageBox(' 非法文件格式', '错误', Mb_Ok + Mb_IconError);

Exit;

end;

GetHead(Fp);

GetFieldNames(Fp);

GetIndex(Fp);

FStream.CopyFrom(Fp, Fp.Size-Fp.Position);

finally

Fp.Free;

end;

end;

procedure TIbStorage.GetHead(Fp: TFileStream);

begin

FRptTitle := ReadAStr(Fp);

FPageHead := ReadAstr(Fp);

FPageFoot := ReadAstr(Fp);

FFieldCount := ReadAInteger(Fp);

FRecordCount := ReadAInteger(Fp);

if ReadAChar(Fp) <> '@' then showmessage('GetHead File Error');

end;

procedure TIbStorage.GetFieldNames(Fp: TFileStream);

var

Ch: Char;

Str: string;

begin

Str := '';

Str := ReadAStr(Fp);

FFieldNames.CommaText := Str;

Ch := ReadAChar(Fp);

if Ch <> '@' then Showmessage('When get fieldnames Error');

end;

procedure TIbStorage.GetIndex(Fp: TFileStream);

var

Ch: Char;

Str: string;

begin

Str := '';

Str := ReadAStr(Fp);

FStreamIndex.CommaText := Str;

Ch := ReadAChar(Fp);

if Ch <> '@' then Showmessage('When Get Field Position Index Error');

end;

//---------Read Field's Value Part

function TIbStorage.GetFieldValue(ARecordNo, FieldNo: Integer): string;

var

Id, T : string;

Pos: Integer;

Len, I : Integer;

Er: Boolean;

begin

Result := '';

Er := False;

if ARecordNo > FRecordCount then

Er := true; //ARecordNo := FRecordCount;

if ARecordNo < 1 then

Er := True; // ARecordNo := 1;

if FieldNo >= FFieldCount then

Er := True; // FieldNo := FFieldCount - 1;

if FieldNo < 0 then

Er := True; //FieldNo := 0;

if Er then

begin

Showmessage('记录号或者字段标号越界');

Exit;

end;

if FFieldCount = 0 then Exit;

Id := Inttostr(ARecordNO) + '_' + IntToStr(FieldNo);

Pos := StrToInt(FStreamIndex.Values[Id]);

FStream.Position := Pos;

//取得字段内容的长度

Len := ReadAInteger(FStream);

if Len > 0 then

Result := ReadBStr(FStream, Len);

if ReadAChar(FStream) <> '@' then

Showmessage('When Read Field, Find Save Format Error');

end;

procedure TIbStorage.FieldStream(ARecordNo, FieldNo: Integer; var AStream: TStream);

var

Id, T : string;

Pos: Integer;

Len, I : Integer;

Er: Boolean;

begin

Er := False;

if ARecordNo > FRecordCount then

Er := true; //ARecordNo := FRecordCount;

if ARecordNo < 1 then

Er := True; // ARecordNo := 1;

if FieldNo >= FFieldCount then

Er := True; // FieldNo := FFieldCount - 1;

if FieldNo < 0 then

Er := True; //FieldNo := 0;

if Er then

begin

TDsException.Create('GetFieldValue函数索引下标越界');

Exit;

end;

if FFieldCount = 0 then Exit;

Id := Inttostr(ARecordNO) + IntToStr(FieldNo);

Pos := StrToInt(FStreamIndex.Values[Id]);

FStream.Position := Pos;

Len := ReadAInteger(FStream);

AStream.CopyFrom(FStream, Len);

end;

function TIbStorage.GetFieldName(AIndex: Integer): string; //取得字段名称

begin

//存储的字段和数据类型各占一半

if ((AIndex < 0) or (AIndex >= FFieldNames.Count div 2)) then

Application.MessageBox(' 取字段名索引越界', '程序 错误',

Mb_Ok + Mb_IconError)

else

Result := FFieldNames.Names[AIndex*2];

end;

function TIbStorage.GetFieldDataType(AIndex: Integer): TFieldType; //取得字段名称

begin

//存储的字段和数据类型各占一半

if ((AIndex < 0) or (AIndex >= FFieldNames.Count div 2)) then

Application.MessageBox(' 取字段数据类型索引越界', '程序 错误',

Mb_Ok + Mb_IconError)

else

Result := TFieldType(StrToInt(FFieldNames.Values[FFieldNames.Names[AIndex*2+1]]));

end;

function TIbStorage.GetDisplayLabel(AIndex: Integer): string; //取得字段显示名称

begin

if ((AIndex < 0) or (AIndex >= FFieldNames.Count)) then

Application.MessageBox(' 取字段名索引越界', '程序 错误',

Mb_Ok + Mb_IconError)

else

Result := FFieldNames.Values[GetFieldName(AIndex)];

end;

end.

通过测试,该控件对Ttable,Tquery, TaodTable, TadoQuery, TibTable, TibQuery等常用的数据集控件等都能较好的支持,并且具有较好的效率(测试:1100条人事记录,23个字段存储为文件约用时2秒钟)。

四、控件的基本使用方法

1.存储数据集中的数据到文件

IbStorage1.Open; //创建存储流

IbStorage1.SaveToFile(AdataSet, Afilename);

2.从文件中读出数据信息

IbStorage1.Open;

IbStorage1.LoadFromFile(AfileName);

3.对数据报存储控件中数据的访问

Value := IbStorage1.Fields[ArecNo, AfieldNo]; //字符串类型

其它略。

五、结束语

通过编写此数据报存储控件,较好地解决了数据库程序中数据的存储和交换问题,为数据库程序的开发提供了一种实用的控件。

该控件在Windows98,Delphi5开发环境下调试通过。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有