1. 概要
Delphi でデータベース プログラムを作成するときは、次のようなデータのインポートとエクスポートの操作が必要になります。たとえば、外部で簡単に読み取れるように、データをポータブル ファイルとして大規模なデータベースに保存します。また、ファイルに保存されているデータを別のデータベースにインポートします。データベースをデータ ファイルとして保存すると、プログラム内およびプログラム間でのデータ交換が容易になり、メモリを介してデータを交換する面倒な手順が回避されます。たとえば、作成者が作成した一般的なレポート プログラムでは、このコントロールはデータ情報媒体として使用されます。配達。
2. 基本的な考え方
データグラム ストレージ コントロールとして、データ セットの基本情報 (フィールド名、フィールド表示名、フィールド データ タイプ、レコード数、フィールド数、指定されたフィールドの現在値など) を格納および読み取りできる必要があります。指定されたレコードなどに)、使いやすさを高めるためにより優れたパッケージング特性を提供できる必要があります。
これをもとに、Delphi5.0のオブジェクト指向の特徴を活かしてデータグラム記憶制御を設計・開発しました。
3. 実施方法
次のコードユニットを記述します。
ユニット IbDbFile;
インタフェース
Windows、SysUtils、クラス、フォーム、Db、DbTables、ダイアログを使用します。
定数
フラグ = 'Datagram-Jixing Software Studio';
タイプ
TDsException = クラス(例外);
TIbStorage = クラス(TComponent)
プライベート
FRptTitle: string; //ストレージデータグラムの説明
FPageHead: string //ページヘッダーの説明
FPageFoot: string //フィートの説明
FFieldNames: TStrings;
FStreamIndex: TStrings;
FStream: TStream; //フィールドのコンテンツを格納するストリーム
FFieldCount: 整数; //フィールドの数;
FRecordCount: 整数;
FOpenFlag: Boolean // ストリームが作成されたかどうかのフラグ
保護された
プロシージャ リセット; //リセット---ストリームの内容をクリアします
プロシージャ SaveHead(ADataSet: TDataSet; Fp: TStream); //ストレージ レポートのヘッダー情報
プロシージャ LoadTableToStream(ADataSet: TDataSet); //ストレージ レコード データ
プロシージャ IndexFields(ADataSet: TDataSet); // データセットのフィールド名をリストに保存します
プロシージャ GetHead(Fp: TFileStream) // レポート ヘッダー情報を保存します。
プロシージャ GetIndex(Fp: TFileStream); // レコード ストリーム インデックスを作成します。
プロシージャ GetFieldNames(Fp: TFileStream); // ストリームからフィールド名テーブルを読み取ります
function GetFieldName(AIndex: Integer): string; //フィールド名を取得します。
関数 GetFieldDataType(AIndex: 整数): TFieldType;
function GetDisplayLabel(AIndex: Integer): string; //フィールドの表示名を取得します。
プロシージャ SaveFieldToStream(AStream: TStream; AField: TField); // フィールドをストリームに保存します。
function GetFieldValue(ARecordNo, FieldNo: Integer): //フィールドの内容
公共
コンストラクター Create(AOwner: TComponent);
デストラクターのオーバーライド。
プロシージャ Open; // データを保存する準備のためのストリームを作成します。
プロシージャ SaveToFile(ADataSet: TDataSet; AFileName: string);
プロシージャ LoadFromFile(AFileName: string); // データをロードします
プロシージャ FieldStream(ARecordNo, FieldNo: 整数; var AStream: TStream);
property FieldNames[Index: Integer]: 文字列読み取り GetFieldName //フィールド名;
プロパティ FieldDataTypes[インデックス: 整数]: TFieldType 読み取り GetFieldDataType;
property FieldDisplayLabels[Index: Integer]: 文字列読み取り GetDisplayLabel;
property Fields[RecNo, FieldIndex: Integer]: 文字列読み取り GetFieldValue;
//プロパティ FieldStreams[RecNo, FieldIndex: Integer]: TStream read GetFieldStream;
property RecordCount: 整数 読み取り FRecordCount 書き込み FRecordCount;
property FieldCount: 整数 読み取り FFieldCount 書き込み FFieldCount;
出版された
プロパティ RptTitle: 文字列 FRptTitle 読み取り FRptTitle 書き込み;
property PageHead: 文字列読み取り FPageHead 書き込み FPageHead;
プロパティ PageFoot: 文字列読み取り FPageFoot 書き込み FPageFoot;
終わり;
関数 ReadAChar(AStream: TStream): Char;
関数 ReadAStr(AStream: TStream): 文字列;
関数 ReadBStr(AStream: TStream; サイズ: 整数): 文字列;
関数 ReadAInteger(AStream: TStream): 整数;
プロシージャ WriteAStr(AStream: TStream; AStr: string);
プロシージャ WriteBStr(AStream: TStream; AStr: string);
プロシージャ WriteAInteger(AStream: TStream; AInteger: Integer);
手順 登録;
実装
手順 登録;
始める
RegisterComponents('データ アクセス', [TIbStorage]);
終わり;
関数 ReadAChar(AStream: TStream): Char;
ヴァール
AChar: シャア。
始める
AStream.Read(AChar, 1);
結果 := AChar;
終わり;
関数 ReadAStr(AStream: TStream): 文字列;
変数
Str: 文字列。
C:チャー;
始める
文字列 := '';
C := ReadAChar(AStream);
一方 C <> #0 は実行します
始める
Str := Str + C;
C := ReadAChar(AStream);
終わり;
結果:= Str;
終わり;
関数 ReadBStr(AStream: TStream; サイズ: 整数): 文字列;
変数
Str: 文字列。
C:チャー;
I : 整数。
始める
文字列 := '';
For I := 1 からサイズを変更します
始める
C := ReadAChar(AStream);
Str := Str + C;
終わり;
結果:= Str;
終わり;
関数 ReadAInteger(AStream: TStream): 整数;
変数
Str: 文字列。
C:チャー;
始める
結果 := MaxInt;
文字列 := '';
C := ReadAChar(AStream);
一方 C <> #0 は実行します
始める
Str := Str + C;
C := ReadAChar(AStream);
終わり;
試す
結果 := StrToInt(Str);
を除外する
application.MessageBox('現在の文字列を整数に変換できません!', 'エラー',
Mb_Ok + Mb_IconError);
終わり;
終わり;
プロシージャ WriteAStr(AStream: TStream; AStr: string);
始める
AStream.Write(ポインタ(AStr)^, 長さ(AStr) + 1);
終わり;
プロシージャ WriteBStr(AStream: TStream; AStr: string);
始める
AStream.Write(Pointer(AStr)^, Length(AStr));
終わり;
プロシージャ WriteAInteger(AStream: TStream; AInteger: Integer);
変数
S: 文字列。
始める
S := IntToStr(AInteger);
WriteAstr(AStream, S);
終わり;
コンストラクター TIbStorage.Create(AOwner: TComponent);
始める
継承された Create(AOwner);
FOpenFlag := False //ストリームが作成されたかどうかを決定するフラグ。
終わり;
デストラクタ TIbStorage.Destroy;
始める
FOpenFlagの場合、
始める
FStream.Free;
FStreamIndex.Free;
FFieldNames.無料;
終わり;
継承された破壊。
終わり;
プロシージャ TIbStorage.Open;
始める
FOpenFlag := True;
FStream := TMemoryStream.Create;
FStreamIndex := TStringList.Create;
FFieldNames := TStringList.Create;
リセット;
終わり;
プロシージャ TIbStorage.Reset;
始める
FOpenFlagの場合、
始める
FFieldNames.Clear;
FStreamIndex.Clear;
FStream.サイズ := 0;
FRptTitle := '';
FPageHead := '';
FPageFoot := '';
FFieldCount := 0;
FRecordCount := 0;
終わり;
終わり;
//------データ部分を保存する
プロシージャ TIbStorage.SaveToFile(ADataSet: TDataSet; AFileName: string);
変数
Fp: TFileStream;
I : 整数。
Ch:シャア。
T1、T2: TDateTime;
文字列: 文字列;
始める
FOpenFlag ではない場合
始める
showmessage('オブジェクトは開いていません');
出口;
終わり;
試す
FileExists(AFileName) の場合は、DeleteFile(AFileName);
Fp := TFileStream.Create(AFileName, fmCreate);
リセット;
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);
ついに
Fp.無料。
終わり;
終わり;
プロシージャ TIbStorage.SaveHead(ADataSet: TDataSet; Fp: TStream);
ヴァール
I : 整数。
Ch:シャア。
始める
ADataSet.Active ではない場合、ADataSet.Active := True;
WriteAStr(Fp, フラグ);
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);
終わり;
プロシージャ TIbStorage.IndexFields(ADataSet: TDataSet);
変数
I : 整数。
Aフィールド: Tフィールド;
始める
I := 0 から ADataSet.Fields.Count - 1 の場合
始める
Aフィールド := ADataSet.Fields[I];
//効率を考慮して FFieldNames.Values[AField.FieldName] := AField.DisplayLabel を使用しません。
FFieldNames.Add(AField.FieldName + '=' + AField.DisplayLabel);
FFieldNames.Add(AField.FieldName + 'DataType=' + IntToStr(Ord(AField.DataType)));
終わり;
終わり;
プロシージャ TIbStorage.LoadTableToStream(ADataSet: TDataSet);
変数
いいえ: 整数。
I、J、サイズ: 整数。
Tmp、Id、Str : //id=string(RecNO) + string(FieldNo);
レン: 整数。
Ch:シャア。
ブロブストリーム: TBlobStream;
始める
FOpenFlag ではない場合
始める
showmessage('オブジェクトは開いていません');
出口;
終わり;
試す
ADataSet.DisableControls;
ADataSet.First;
いいえ:= 0;
FStreamIndex.Clear;
FStream.サイズ := 0;
ADataSet.Eof ではないが、
始める
いいえ := いいえ + 1;
J の場合:= 0 から ADataSet.Fields.Count - 1 を実行します
始める
ID := Inttostr(NO) + '_' + IntToStr(J);
//ストリームの位置のインデックスを作成します。インデックスは次を指します: Size#0Content
FStreamIndex.Add(Id + '=' + IntToStr(FStream.Position));
//ストレージフィールド情報をストリームに取り込む
SaveFieldToStream(FStream, ADataSet.Fields[J]);
終わり;
ADataSet.Next;
終わり;
ついに
ADataSet.EnableControls;
終わり;
終わり;
//フィールドの現在のコンテンツが空であるか、BlobSize<=0 の場合、フィールド サイズのみが 0 になり、コンテンツは書き込まれません。
プロシージャ TIbStorage.SaveFieldToStream(AStream: TStream; AField: TField);
変数
サイズ: 整数。
Ch:シャア。
XF: Tストリーム;
文字列: 文字列;
始める
AField.IsBlob の場合
始める
//TBlobField フィールドの内容をストリームとして保存する方法
Xf := TBlobStream.Create(TBlobField(AField), bmread);
試す
Xf.Size > 0 の場合
始める
サイズ := Xf.サイズ;
WriteAInteger(AStream, サイズ);
AStream.CopyFrom(Xf, Xf.Size);
終わり
それ以外
WriteAInteger(AStream, 0);
ついに
XF.無料。
終わり;
終わり
それ以外
始める
Str := AField.AsString;
サイズ := 長さ(Str);
WriteAInteger(AStream, サイズ);
サイズ <> 0 の場合
AStream.Write(Pointer(Str)^, Size);
//WriteAstr(AStream, Str);
終わり;
Ch := '@';
AStream.Write(Ch, 1);
終わり;
//---------------データのロード
プロシージャ TIbStorage.LoadFromFile(AFileName: string);
変数
Fp: TFileStream;
チェック: 文字列;
始める
リセット;
試す
ファイルが存在しない場合(AFileName)
始める
showmessage('ファイルが存在しません:' + AFileName);
出口;
終わり;
Fp := TFileStream.Create(AFileName, fmOpenRead);
チェック := ReadAStr(Fp);
if <> フラグをチェックしてから
始める
Application.MessageBox('不正なファイル形式', 'エラー', Mb_Ok + Mb_IconError);
出口;
終わり;
GetHead(Fp);
GetFieldNames(Fp);
GetIndex(Fp);
FStream.CopyFrom(Fp, Fp.Size-Fp.Position);
ついに
Fp.無料。
終わり;
終わり;
プロシージャ TIbStorage.GetHead(Fp: TFileStream);
始める
FRptTitle := ReadAStr(Fp);
FPageHead := ReadAstr(Fp);
FPageFoot := ReadAstr(Fp);
FFieldCount := ReadAInteger(Fp);
FRecordCount := ReadAInteger(Fp);
if ReadAChar(Fp) <> '@' then showmessage('GetHead File Error');
終わり;
プロシージャ TIbStorage.GetFieldNames(Fp: TFileStream);
変数
Ch:シャア。
文字列: 文字列;
始める
文字列 := '';
Str := ReadAStr(Fp);
FFieldNames.CommaText := Str;
Ch := ReadAChar(Fp);
if Ch <> '@' then Showmessage('フィールド名取得エラー時');
終わり;
プロシージャ TIbStorage.GetIndex(Fp: TFileStream);
変数
Ch:シャア。
文字列: 文字列;
始める
文字列:= '';
Str := ReadAStr(Fp);
FStreamIndex.CommaText := Str;
Ch := ReadAChar(Fp);
if Ch <> '@' then Showmessage('フィールド位置インデックス取得エラー時');
終わり;
//----------フィールドの値部分を読み取る
関数 TIbStorage.GetFieldValue(ARecordNo, FieldNo: 整数): 文字列;
変数
ID、T : 文字列;
位置: 整数;
Len、I : 整数;
Er: ブール値。
始める
結果 := '';
Er := False;
if ARecordNo > FRecordCount then
Er := true //ARecordNo := FRecordCount;
ARecordNo < 1 の場合
Er := True // ARecordNo := 1;
FieldNo >= FFieldCount の場合
Er := True; // フィールド番号 := FFieldCount - 1;
ifFieldNo < 0 then
Er := True //フィールド番号 := 0;
えーなら、それでは
始める
Showmessage('レコード番号またはフィールド ラベルが範囲外です');
出口;
終わり;
FFieldCount = 0 の場合は終了します。
ID := Inttostr(ARecordNO) + '_' + IntToStr(FieldNo);
Pos := StrToInt(FStreamIndex.Values[Id]);
FStream.Position := 位置;
//フィールド内容の長さを取得する
Len := ReadAInteger(FStream);
Len > 0 の場合
結果 := ReadBStr(FStream, Len);
if ReadAChar(FStream) <> '@' then
Showmessage('フィールドを読み取ると、保存形式エラーが見つかります');
終わり;
プロシージャ TIbStorage.FieldStream(ARecordNo, FieldNo: Integer; var AStream: TStream);
変数
ID、T : 文字列;
位置: 整数;
Len、I : 整数;
Er: ブール値。
始める
Er := False;
if ARecordNo > FRecordCount then
Er := true //ARecordNo := FRecordCount;
ARecordNo < 1 の場合
Er := True // ARecordNo := 1;
FieldNo >= FFieldCount の場合
Er := True // フィールド番号 := FFieldCount - 1;
ifFieldNo < 0 then
Er := True //フィールド番号 := 0;
えーなら、それでは
始める
TDsException.Create('GetFieldValue 関数インデックスの添字が範囲外です');
出口;
終わり;
FFieldCount = 0 の場合は終了します。
ID := Inttostr(ARecordNO) + IntToStr(FieldNo);
Pos := StrToInt(FStreamIndex.Values[Id]);
FStream.Position := 位置;
Len := ReadAInteger(FStream);
AStream.CopyFrom(FStream, Len);
終わり;
function TIbStorage.GetFieldName(AIndex: Integer): //フィールド名を取得します。
始める
// 保存されたフィールドとデータ型がそれぞれ半分を占めます
if ((AIndex < 0) または (AIndex >= FFieldNames.Count div 2)) then
Application.MessageBox('フィールド名インデックスが範囲外です', 'プログラム エラー',
Mb_Ok + Mb_IconError)
それ以外
結果 := FFieldNames.Names[AIndex*2];
終わり;
function TIbStorage.GetFieldDataType(AIndex: Integer): TFieldType; //フィールド名を取得します。
始める
// 保存されたフィールドとデータ型がそれぞれ半分を占めます
if ((AIndex < 0) または (AIndex >= FFieldNames.Count div 2)) then
Application.MessageBox('フィールドのデータ型インデックスが範囲外です', 'プログラム エラー',
Mb_Ok + Mb_IconError)
それ以外
結果 := TFieldType(StrToInt(FFieldNames.Values[FFieldNames.Names[AIndex*2+1]]));
終わり;
function TIbStorage.GetDisplayLabel(AIndex: Integer): string; //フィールドの表示名を取得します。
始める
if ((AIndex < 0) または (AIndex >= FFieldNames.Count)) then
Application.MessageBox('フィールド名インデックスが範囲外です', 'プログラム エラー',
Mb_Ok + Mb_IconError)
それ以外
結果 := FFieldNames.Values[GetFieldName(AIndex)];
終わり;
終わり。
テストを通じて、このコントロールは、Ttable、Tquery、TaodTable、TadoQuery、TibTable、TibQuery などの一般的に使用されるデータ セット コントロールを適切にサポートしており、効率も良好です (テスト: 1100 人の従業員レコード、23 フィールドをファイルとして保存すると、約 2 時間がかかります)秒)。
4. コントロールの基本的な使い方
1.データセットからファイルにデータを保存する
IbStorage1.Open //ストレージストリームを作成します。
IbStorage1.SaveToFile(AdataSet, Afilename);
2.ファイルからデータ情報を読み取る
IbStorage1.Open;
IbStorage1.LoadFromFile(ファイル名);
3.データグラムストレージ制御内のデータへのアクセス
値 := IbStorage1.Fields[AlecNo, AfieldNo] //文字列型
他は省略しています。
5. 結論
このデータグラム記憶制御を記述することにより、データベース プログラムにおけるデータの記憶と交換の問題がより適切に解決され、データベース プログラムの開発に実用的な制御が提供されます。
このコントロールは、Windows98 および Delphi5 開発環境でのデバッグに合格しました。