1. Übersicht
Beim Schreiben von Datenbankprogrammen in Delphi sind häufig Datenimport- und -exportvorgänge erforderlich, z. B. das Speichern von Daten in einer großen Datenbank als tragbare Dateien zum einfachen Lesen von Daten in einer anderen Datenbank Durch die Verwendung von Datendateien in der Datenbank ist es einfacher, Daten innerhalb und zwischen Programmen auszutauschen und die umständlichen Schritte des Datenaustauschs über den Speicher zu vermeiden. In dem vom Autor geschriebenen allgemeinen Berichtsprogramm wird dieses Steuerelement beispielsweise als Dateninformationsträger verwendet Lieferung.
2. Grundideen
Als Datagramm-Speichersteuerung sollte es in der Lage sein, die grundlegenden Informationen des Datensatzes zu speichern und zu lesen (z. B. Feldname, Feldanzeigename, Felddatentyp, Anzahl der Datensätze, Anzahl der Felder, aktueller Wert des angegebenen Felds). im angegebenen Datensatz usw.) und sollte bessere Verpackungseigenschaften für eine einfachere Verwendung bieten.
Auf dieser Grundlage nutzte der Autor die objektorientierten Eigenschaften von Delphi5.0, um die Datagramm-Speichersteuerung zu entwerfen und zu entwickeln.
3. Implementierungsmethode
Schreiben Sie die folgende Codeeinheit:
Einheit IbDbFile;
Schnittstelle
Verwendet Windows, SysUtils, Klassen, Forms, Db, DbTables, Dialoge;
Konst
Flag = 'Datagram-Jixing Software Studio';
Typ
TDsException = Class(Exception);
TIbStorage = class(TComponent)
Privat
FRptTitle: string; //Beschreibung des Speicherdatagramms
FPageHead: string; //Seitenkopfbeschreibung
FPageFoot: string; //Beschreibung der Füße
FFieldNames: TStrings; //Feldnamentabelle
FStreamIndex: TStrings; //Feldindex
FStream: TStream; //Stream, der Feldinhalte speichert
FFieldCount: Integer; //Anzahl der Felder
FRecordCount: Integer; //Anzahl der Datensätze
FOpenFlag: Boolean; // Ob der Stream erstellt wird
geschützt
procedure Reset; //Reset---löscht den Inhalt des Streams
procedure SaveHead(ADataSet: TDataSet; Fp: TStream); //Speicherbericht-Header-Informationen
procedure LoadTableToStream(ADataSet: TDataSet); //Speicherdatensatzdaten
procedure IndexFields(ADataSet: TDataSet); //Speichern Sie die Feldnamen des Datensatzes in der Liste
procedure GetHead(Fp: TFileStream); //Berichtsheaderinformationen speichern
procedure GetIndex(Fp: TFileStream); //Datenstromindex erstellen
procedure GetFieldNames(Fp: TFileStream); //Lesen Sie die Feldnamentabelle aus dem Stream
function GetFieldName(AIndex: Integer): string; //Den Feldnamen abrufen
function GetFieldDataType(AIndex: Integer): TFieldType;
function GetDisplayLabel(AIndex: Integer): string; //Den Feldanzeigenamen abrufen
procedure SaveFieldToStream(AStream: TStream; AField: TField); //Speichern Sie das Feld im Stream
function GetFieldValue(ARecordNo, FieldNo: Integer): string //Feldinhalt
öffentlich
Konstruktor Create(AOwner: TComponent);
Destruktor zerstören; überschreiben;
procedure Open; // Einen Stream erstellen, um die Datenspeicherung vorzubereiten
procedure SaveToFile(ADataSet: TDataSet; AFileName: string); //Storage-Methode
procedure LoadFromFile(AFileName: string); //Daten laden
procedure FieldStream(ARecordNo, FieldNo: Integer; var AStream: TStream);
Eigenschaft FieldNames[Index: Integer]: string read GetFieldName; //Feldname
Eigenschaft FieldDataTypes[Index: Integer]: TFieldType read GetFieldDataType;
Eigenschaft FieldDisplayLabels[Index: Integer]: string read GetDisplayLabel;
Eigenschaft Fields[RecNo, FieldIndex: Integer]: string read GetFieldValue;
//property FieldStreams[RecNo, FieldIndex: Integer]: TStream read GetFieldStream;
Eigenschaft RecordCount: Integer read FRecordCount write FRecordCount;
Eigenschaft FieldCount: Integer read FFieldCount write FFieldCount;
veröffentlicht
Eigenschaft RptTitle: String read FRptTitle write FRptTitle;
Eigenschaft PageHead: String read FPageHead write FPageHead;
Eigenschaft PageFoot: String read FPageFoot write FPageFoot;
Ende;
Funktion ReadAChar(AStream: TStream): Char;
Funktion ReadAStr(AStream: TStream): string;
function ReadBStr(AStream: TStream; Size: Integer): string;
Funktion ReadAInteger(AStream: TStream): Integer;
procedure WriteAStr(AStream: TStream; AStr: string);
procedure WriteBStr(AStream: TStream; AStr: string);
procedure WriteAInteger(AStream: TStream; AInteger: Integer);
Verfahren Registrieren;
Durchführung
Verfahren Registrieren;
beginnen
RegisterComponents('Datenzugriff', [TIbStorage]);
Ende;
Funktion ReadAChar(AStream: TStream): Char;
Var
AChar: Char;
beginnen
AStream.Read(AChar, 1);
Ergebnis := AChar;
Ende;
Funktion ReadAStr(AStream: TStream): string;
var
Str: String;
C:Char;
beginnen
Str := '';
C := ReadAChar(AStream);
Während C <> #0 dies tut
beginnen
Str := Str + C;
C := ReadAChar(AStream);
Ende;
Ergebnis := Str;
Ende;
function ReadBStr(AStream: TStream; Size: Integer): string;
var
Str: String;
C:Char;
I: Ganzzahl;
beginnen
Str := '';
Für I := 1 bis Größe tun
beginnen
C := ReadAChar(AStream);
Str := Str + C;
Ende;
Ergebnis := Str;
Ende;
Funktion ReadAInteger(AStream: TStream): Integer;
var
Str: String;
C:Char;
beginnen
Ergebnis := MaxInt;
Str := '';
C := ReadAChar(AStream);
Während C <> #0 dies tut
beginnen
Str := Str + C;
C := ReadAChar(AStream);
Ende;
versuchen
Ergebnis := StrToInt(Str);
außer
application.MessageBox('Die aktuelle Zeichenfolge kann nicht in eine Ganzzahl konvertiert werden!', 'Fehler',
Mb_Ok + Mb_IconError);
Ende;
Ende;
procedure WriteAStr(AStream: TStream; AStr: string);
beginnen
AStream.Write(Pointer(AStr)^, Length(AStr) + 1);
Ende;
procedure WriteBStr(AStream: TStream; AStr: string);
beginnen
AStream.Write(Pointer(AStr)^, Length(AStr));
Ende;
procedure WriteAInteger(AStream: TStream; AInteger: Integer);
var
S: Schnur;
beginnen
S := IntToStr(AInteger);
WriteAstr(AStream, S);
Ende;
Konstruktor TIbStorage.Create(AOwner: TComponent);
beginnen
geerbt Create(AOwner);
FOpenFlag := False; //Flag, um zu bestimmen, ob der Stream erstellt wird
Ende;
Destruktor TIbStorage.Destroy;
beginnen
wenn FOpenFlag dann
beginnen
FStream.Free;
FStreamIndex.Free;
FFieldNames.Free;
Ende;
geerbt Zerstören;
Ende;
Prozedur TIbStorage.Open;
beginnen
FOpenFlag := True;
FStream := TMemoryStream.Create;
FStreamIndex := TStringList.Create;
FFieldNames := TStringList.Create;
Zurücksetzen;
Ende;
procedure TIbStorage.Reset; //reset
beginnen
wenn FOpenFlag dann
beginnen
FFieldNames.Clear;
FStreamIndex.Clear;
FStream.Size := 0;
FRptTitle := '';
FPageHead := '';
FPageFoot := '';
FFieldCount := 0;
FRecordCount := 0;
Ende;
Ende;
//-------Datenteil speichern
procedure TIbStorage.SaveToFile(ADataSet: TDataSet; AFileName: string);
var
Fp: TFileStream;
I: Ganzzahl;
Ch: Char;
T1, T2: TDateTime;
Str: string;
beginnen
Wenn nicht FOpenFlag, dann
beginnen
showmessage('Das Objekt ist nicht geöffnet');
Ausfahrt;
Ende;
versuchen
if FileExists(AFileName) then DeleteFile(AFileName);
Fp := TFileStream.Create(AFileName, fmCreate);
Zurücksetzen;
SaveHead(ADataSet, Fp); //Speichern Sie die Header-Informationen ---Zusätzliche Anweisungen
IndexFields(ADataSet); //Speichern Sie die Feldinformationen des Datensatzes in FFieldName
LoadTableToStream(ADataSet); //Speichern Sie die Dateninformationen des Datensatzes
WriteAStr(Fp, FFieldNames.Text); //Informationen zum Speicherfeldnamen
Ch := '@';
Fp.Write(Ch, 1);
WriteAStr(Fp, FStreamIndex.Text); //Speicherfeldindexliste
Ch := '@';
Fp.Write(Ch, 1);
Fp.CopyFrom(FStream, 0);
Endlich
Fp.Free;
Ende;
Ende;
procedure TIbStorage.SaveHead(ADataSet: TDataSet; Fp: TStream);
Var
I: Ganzzahl;
Ch: Char;
beginnen
wenn nicht ADataSet.Active, dann 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);
Ende;
procedure TIbStorage.IndexFields(ADataSet: TDataSet);
var
I: Ganzzahl;
AField: TField;
beginnen
Für I := 0 bis ADataSet.Fields.Count - 1 tun
beginnen
AField := ADataSet.Fields[I];
//Die Nichtverwendung von FFieldNames.Values[AField.FieldName] := AField.DisplayLabel; dient der Berücksichtigung der Effizienz
FFieldNames.Add(AField.FieldName + '=' + AField.DisplayLabel);
FFieldNames.Add(AField.FieldName + 'DataType=' + IntToStr(Ord(AField.DataType)));
Ende;
Ende;
procedure TIbStorage.LoadTableToStream(ADataSet: TDataSet);
var
Nein: Ganzzahl;
I, J, Größe: Ganzzahl;
Tmp, Id, Str : string; //id=string(RecNO) + string(FieldNo)
Länge: Ganzzahl;
Ch: Char;
BlobStream: TBlobStream;
beginnen
Wenn nicht FOpenFlag, dann
beginnen
showmessage('Das Objekt ist nicht geöffnet');
Ausfahrt;
Ende;
versuchen
ADataSet.DisableControls;
ADataSet.First;
Nein := 0;
FStreamIndex.Clear;
FStream.Size := 0;
Während ADataSet.Eof dies nicht tut
beginnen
Nein := Nein + 1;
Für J := 0 bis ADataSet.Fields.Count - 1 tun
beginnen
Id := Inttostr(NO) + '_' + IntToStr(J);
//Erstelle einen Index der Position des Streams, der Index zeigt auf: Size#0Content
FStreamIndex.Add(Id + '=' + IntToStr(FStream.Position));
//Feldinformationen im Stream speichern
SaveFieldToStream(FStream, ADataSet.Fields[J]);
Ende;
ADataSet.Next;
Ende;
Endlich
ADataSet.EnableControls;
Ende;
Ende;
//Wenn der aktuelle Inhalt eines Feldes leer ist oder BlobSize<=0, ist nur die Feldgröße 0 und es wird kein Inhalt geschrieben.
procedure TIbStorage.SaveFieldToStream(AStream: TStream; AField: TField);
var
Größe: Ganzzahl;
Ch: Char;
XF: TStream;
Str: string;
beginnen
wenn AField.IsBlob dann
beginnen
//So speichern Sie den Inhalt eines TBlobField-Felds als Stream
Xf := TBlobStream.Create(TBlobField(AField), bmread);
versuchen
wenn Xf.Size > 0 dann
beginnen
Größe := Xf.Size;
WriteAInteger(AStream, Size);
AStream.CopyFrom(Xf, Xf.Size);
Ende
anders
WriteAInteger(AStream, 0);
Endlich
XF.Free;
Ende;
Ende
anders
beginnen
Str := AField.AsString;
Größe := Länge(Str);
WriteAInteger(AStream, Size);
wenn Größe <> 0 dann
AStream.Write(Pointer(Str)^, Size);
//WriteAstr(AStream, Str);
Ende;
Ch := '@';
AStream.Write(Ch, 1);
Ende;
//------------Daten laden
procedure TIbStorage.LoadFromFile(AFileName: string);
var
Fp: TFileStream;
Prüfen: string;
beginnen
Zurücksetzen;
versuchen
wenn nicht FileExists(AFileName), dann
beginnen
showmessage('Datei existiert nicht:' + AFileName);
Ausfahrt;
Ende;
Fp := TFileStream.Create(AFileName, fmOpenRead);
Prüfen := ReadAStr(Fp);
wenn Check <> Flag dann
beginnen
Application.MessageBox('Ungültiges Dateiformat', 'Fehler', Mb_Ok + Mb_IconError);
Ausfahrt;
Ende;
GetHead(Fp);
GetFieldNames(Fp);
GetIndex(Fp);
FStream.CopyFrom(Fp, Fp.Size-Fp.Position);
Endlich
Fp.Free;
Ende;
Ende;
procedure TIbStorage.GetHead(Fp: TFileStream);
beginnen
FRptTitle := ReadAStr(Fp);
FPageHead := ReadAstr(Fp);
FPageFoot := ReadAstr(Fp);
FFieldCount := ReadAInteger(Fp);
FRecordCount := ReadAInteger(Fp);
if ReadAChar(Fp) <> '@' then showmessage('GetHead File Error');
Ende;
procedure TIbStorage.GetFieldNames(Fp: TFileStream);
var
Ch: Char;
Str: string;
beginnen
Str := '';
Str := ReadAStr(Fp);
FFieldNames.CommaText := Str;
Ch := ReadAChar(Fp);
if Ch <> '@' then Showmessage('When get fieldnames Error');
Ende;
procedure TIbStorage.GetIndex(Fp: TFileStream);
var
Ch: Char;
Str: string;
beginnen
Str := '';
Str := ReadAStr(Fp);
FStreamIndex.CommaText := Str;
Ch := ReadAChar(Fp);
if Ch <> '@' then Showmessage('When Get Field Position Index Error');
Ende;
//---------Wertteil des Feldes lesen
Funktion TIbStorage.GetFieldValue(ARecordNo, FieldNo: Integer): string;
var
Id, T: Zeichenfolge;
Pos: Ganzzahl;
Len, I: Ganzzahl;
Er: Boolescher Wert;
beginnen
Ergebnis := '';
Äh := Falsch;
wenn ARecordNo > FRecordCount dann
Er := true; //ARecordNo := FRecordCount;
wenn ARecordNo < 1 dann
Er := True; // ARecordNo := 1;
wenn FieldNo >= FFieldCount dann
Er := True; // FieldNo := FFieldCount - 1;
ifFieldNo < 0 dann
Er := True; //FieldNo := 0;
wenn Äh dann
beginnen
Showmessage('Die Datensatznummer oder Feldbezeichnung liegt außerhalb der Grenzen');
Ausfahrt;
Ende;
wenn FFieldCount = 0 dann Exit;
Id := Inttostr(ARecordNO) + '_' + IntToStr(FieldNo);
Pos := StrToInt(FStreamIndex.Values[Id]);
FStream.Position := Pos;
//Ermitteln Sie die Länge des Feldinhalts
Länge := ReadAInteger(FStream);
wenn Len > 0 dann
Ergebnis := ReadBStr(FStream, Len);
wenn ReadAChar(FStream) <> '@' dann
Showmessage('Wenn Sie das Feld lesen, finden Sie einen Speicherformatfehler');
Ende;
procedure TIbStorage.FieldStream(ARecordNo, FieldNo: Integer; var AStream: TStream);
var
Id, T: Zeichenfolge;
Pos: Ganzzahl;
Len, I: Ganzzahl;
Er: Boolescher Wert;
beginnen
Äh := Falsch;
wenn ARecordNo > FRecordCount dann
Er := true; //ARecordNo := FRecordCount;
wenn ARecordNo < 1 dann
Er := True; // ARecordNo := 1;
wenn FieldNo >= FFieldCount dann
Er := True; // FieldNo := FFieldCount - 1;
ifFieldNo < 0 dann
Er := True; //FieldNo := 0;
wenn Äh dann
beginnen
TDsException.Create('GetFieldValue-Funktionsindexindex außerhalb der Grenzen');
Ausfahrt;
Ende;
wenn FFieldCount = 0 dann Exit;
Id := Inttostr(ARecordNO) + IntToStr(FieldNo);
Pos := StrToInt(FStreamIndex.Values[Id]);
FStream.Position := Pos;
Länge := ReadAInteger(FStream);
AStream.CopyFrom(FStream, Len);
Ende;
function TIbStorage.GetFieldName(AIndex: Integer): string; //Den Feldnamen abrufen
beginnen
//Die gespeicherten Felder und Datentypen machen jeweils die Hälfte aus
if ((AIndex < 0) oder (AIndex >= FFieldNames.Count div 2)) dann
Application.MessageBox('Feldnamenindex außerhalb der Grenzen', 'Programmfehler',
Mb_Ok + Mb_IconError)
anders
Ergebnis := FFieldNames.Names[AIndex*2];
Ende;
function TIbStorage.GetFieldDataType(AIndex: Integer): TFieldType; //Den Feldnamen abrufen
beginnen
//Die gespeicherten Felder und Datentypen machen jeweils die Hälfte aus
if ((AIndex < 0) oder (AIndex >= FFieldNames.Count div 2)) dann
Application.MessageBox('Felddatentypindex liegt außerhalb der Grenzen', 'Programmfehler',
Mb_Ok + Mb_IconError)
anders
Ergebnis := TFieldType(StrToInt(FFieldNames.Values[FFieldNames.Names[AIndex*2+1]]));
Ende;
function TIbStorage.GetDisplayLabel(AIndex: Integer): string; //Den Feldanzeigenamen abrufen
beginnen
if ((AIndex < 0) oder (AIndex >= FFieldNames.Count)) dann
Application.MessageBox('Feldnamenindex außerhalb der Grenzen', 'Programmfehler',
Mb_Ok + Mb_IconError)
anders
Ergebnis := FFieldNames.Values[GetFieldName(AIndex)];
Ende;
Ende.
Durch Tests bietet dieses Steuerelement eine gute Unterstützung für häufig verwendete Datensatzsteuerelemente wie Ttable, Tquery, TaodTable, TadoQuery, TibTable, TibQuery usw. und weist eine gute Effizienz auf (Test: 1100 Personaldatensätze, 23 als Datei gespeicherte Felder, dauert etwa 2 Sekunden).
4. Grundlegende Verwendung von Steuerelementen
1. Speichern Sie Daten aus einem Datensatz in einer Datei
IbStorage1.Open; //Speicherstrom erstellen
IbStorage1.SaveToFile(AdataSet, Afilename);
2. Dateninformationen aus der Datei lesen
IbStorage1.Open;
IbStorage1.LoadFromFile(AfileName);
3. Zugriff auf Daten in der Datagramm-Speichersteuerung
Value := IbStorage1.Fields[ArecNo, AfieldNo]; //String-Typ
Andere werden weggelassen.
5. Fazit
Durch das Schreiben dieser Datagramm-Speichersteuerung wird das Problem der Datenspeicherung und des Datenaustauschs in Datenbankprogrammen besser gelöst und eine praktische Steuerung für die Entwicklung von Datenbankprogrammen bereitgestellt.
Das Steuerelement hat das Debugging unter Windows98 und der Delphi5-Entwicklungsumgebung bestanden.