Einzelnen Beitrag anzeigen

Benutzerbild von Chemiker
Chemiker

Registriert seit: 14. Aug 2005
1.858 Beiträge
 
Delphi 11 Alexandria
 

Records in einem FileStream speichern

  Alt 19. Jan 2007, 23:53
Records in einem FileStreams speichern.

Hallo,

das ist mein erstes Tutorial, deshalb last Gnade vor Recht ergehen.

Damit Daten auch dem Beenden eines Programms erhalten bleiben, müssen sie dauerhaft vorliegen, sprich sie müssen auf einen Datenträger, vorzugsweise auf eine Festplatte abgespeichert werden.

Dieses Beispielprogramm besteht aus 3 Units.

1. Const_DatUnit / Hier werden globale Konstanten abgelegt.
2. StreamUnit / Hier sind die TFileStrem-Funktionen abgelegt
3. HauptprogrammUnit1 / Hier werden die Funktionen angewendet.

Bewusst wurden auf komplexre Objekte versichtet und im Sinne der Grundlagenvermittlung weitestgehend auf elementare Bausteine zurückgegriffen.

In unserem Beispiel werden wir auf einen benutzerdefinierten Datentyp (Record) zurückgreifen.

Da es in der Regel üblich ist, bei Records mit einer Adressen-Datenbank zu arbeiten, habe ich mich dazu entschlossen von dieser Regel einmal abzuweichen.

Ich werde in diesem Beispiel Messwerte auf die Festplatte abspeichern, diese Messwerte (kurz: MW) werden normalerweise von einer SPS übermittelt. In unserem Fall müssen wir sie leider selber eingeben.

Der Record besteht aus:

1. Maschinen-Nr. also an welcher Maschine ist der MW angefallen
2. Messstelle-Nr. an welcher Messstelle dieser Maschine ist der
MW gemessen worden
3. Messwert der eigentliche MW
4. Datum/Uhrzeit Wann ist der MW angefallen sprich Datum und
Uhrzeit
5. Messwert Gelesen Flag Ist der MW schon in eine Datenbank
übertragen werden.
6. Messwert Flag Status Flag


Delphi-Quellcode:
{
********************************************************************************
* Unit: Const_DatUnit                                                          *
* letz.Änderung: 15.01.2007                                                    *
*------------------------------------------------------------------------------*
* Beschreibung: In dieser Unit werden die Konstanten für                      *
*              StreamVerwaltungProject deklariert                            *
********************************************************************************
}

unit Const_DatUnit;

interface

Uses Classes;

const

  // Konstanten für die Strings
  MA_ZEICH_ANZ = 3;
  MS_ZEICH_ANZ = 2;

  // Datenbezeichnungen
  LW_BEZ = 'c:\'; // Laufwerks-Bezeichnung
  ORDNER_NAME = 'StreamDatenOrdner'; // Verzeichnis-Name
  DATEI_NAME = '\MessWertDaten'; // Name der Datei
  DATEI_ERW = '.dxt'; // Datei-Erweiterung

  ORDNER_PFAD = LW_BEZ + ORDNER_NAME;
  STREAM_PFAD = LW_BEZ + ORDNER_NAME + DATEI_NAME + DATEI_ERW;

type
{
********************************************************************************
* Der Daten-Record der als Datensatz verarbeitet werden soll.                  *
********************************************************************************
}

  TMW_Stream_Rec = Record
    stfMaNr: string[MA_ZEICH_ANZ]; // Maschinen-Nr.
    stfMs : string[MS_ZEICH_ANZ]; // Mess-Stellen-Nr.
    dblMw : double; // Mess-Wert als double
    dtDatumUhrzeit: TDateTime; // System Datum u. Uhrzeit als der MW
                                      // übertragen worden ist.
    blnMWGelesen: boolean; // bei TRUE ist der Wert in die Daten-
                                      // bank übertragen worden.
    blnMWFlag: boolean; // Status des Records: TRUE= Fehler
  end;
{------------------------------------------------------------------------------}
{
********************************************************************************
* Globale Variable                                                            *
********************************************************************************
}

var
  DateiName: String; // Hier wird der Stream_Pfad abgespeichert


implementation

end.
Das ganze soll ja mit einem FileStream – Objekt auf die Festplatte gespeichert werden, des halben werden wir folgende Methoden behandeln:

TFileStream.Create
TFileStream.free
TFileStream.Position
TFileStream.Seek
TFileStream.WriteBuffer
TFileStream.ReadBuffer
TFileStream.Size


Diese Methoden werden in der Unit: StreamUnit benutzt.

Delphi-Quellcode:
{
********************************************************************************
* Unit: StreamUnit                                                            *
* letz. Änderung: 17.01.2007                                                  *
*------------------------------------------------------------------------------*
}

unit StreamUnit;

interface

Uses SysUtils, Classes, Dialogs, Forms, FileCtrl,
     Const_DatUnit;

function Ordner_anlegen (OrdnerName: string): boolean;
function Stream_oeffnen (var f: TFileStream ;const DateiName: string): boolean;
function Stream_Schliessen (var f:TFileStream): boolean;
function Stream_MW_Rec_Anhaengen
                           (var f: TFileStream; MWRec: TMW_Stream_Rec): boolean;
function Stream_MW_Rec_Lesen (var f: TFileStream; var MWRec: TMW_Stream_Rec;
                              DatensatzNr: integer): boolean;
function Stream_MW_Rec_DatenSatzAnzahl
                   (var f: TFileStream; MWRec: TMW_Stream_Rec): integer;
function Stream_MW_Rec_Aendern (var f: TFileStream; MWRec: TMW_Stream_Rec;
                                DatenSatzNr: integer): boolean;
implementation
{
********************************************************************************
* Ordner_anlegen(): Mit dieser Function wird der Ordner angelegt              *
**----------------------------------------------------------------------------**
* Eingabe: Ordner-Name                                                        *
* FunctionsWert: TRUE = Der Ordner war schon vorhanden                        *
*                FALSE = Der Ordner wurde neu angelegt                        *
* Beschreibung: Mit dieser Funktion wird geprüft, ob der Ordner der in        *
*              OrdnerName angegeben worden ist vorhanden ist. Ist er nicht    *
*              vorhanden so wird er neu angelegt.                            *
*              Wenn der Ordner nicht angelegt werden konnte, wird eine Fehler-*
*              Meldung angezeigt.                                            *
********************************************************************************
}

function Ordner_anlegen (OrdnerName: string): boolean;
begin
  if not DirectoryExists(OrdnerName) then
    if not CreateDir(OrdnerName) then
      begin
        raise Exception.Create('Der Ordner '+OrdnerName+
                               ' wurde nicht angelegt');
      end
     else
      begin
        result:= FALSE; // Ordner neu angelegt
      end
    else
      result:= TRUE; // Ordner war schon vorhanden
end;
{------------------------------------------------------------------------------}
{
********************************************************************************
* FileStream Functionen und Proceduren                                        *
*------------------------------------------------------------------------------*
* letz.Änderung: 17.01.2007                                                    *
********************************************************************************
}

{
********************************************************************************
* Stream_oeffnen(): Mit dieser Function wird ein Stream geöffnet              *
**-----------------------------------------------------------------------------*
* Eingabe: var f: TFileStream                                                  *
*          const DateiName: string // Der DateiName muss komplett mit einem    *
*                                    Path sein!                              *
* FunctionsWert: TRUE = Der Stream ist zum lesen und schreiben geöffnet worden *
*                FALSE = Der Stream (DateiName ist neu angelegt worden        *
* Beschreibung: Mit dieser Function wird der Stream geöffnet. Ist er bis jetzt *
*              nicht vorhanden gewesen, so wird er neu angelegt.              *
*              Die Dateien sind ohne Einschränkungen zum Lesen und Schreiben  *
*              Dies wird mit FmShareDenyNone eingestellt.                    *
********************************************************************************
}

function Stream_oeffnen (var f: TFileStream ;const DateiName: string): boolean;
begin
  result:= TRUE;
  try
    {-- FileStream zum Lesen u. Schreiben uneingeschränkt öffnen    -----------}
    f:= TFileStream.Create(DateiName, fmOpenReadWrite or fmShareDenyNone)
  except
    try
     { FileStream neu anlegen, tritt hier ein Fehler auf so wird das Prg. ab-  }
     { gebrochen und zur der aufrufende Routine zurückgesprungen.              }
     f:= TFileStream.Create(DateiName, fmCreate or fmShareDenyNone);
    except
      ShowMessage('Fehler beim Erzeugen der Datei:'+#13+DateiName);
      Exit; // Zur aufrufenden Routine zurück.
    end;
    result:= FALSE;
  end;
end;
{
********************************************************************************
* Stream_Schliessen(): Mit dieser Function wird ein Stream geschlossen        *
**-----------------------------------------------------------------------------*
* Eingabe: var f: TFileStream                                                  *
* FunctionsWert: TRUE = Alles O.K. der Stream ist geschlossen worden.          *
*                FALSE = Es ist ein Fehler beim Schliessen des Streams aufge-  *
*                        treten. Wahrscheinlich war der Stream nicht geöffnet. *
* Beschreibung: Mit dieser Function wird der Stream geschlossen.              *
*              War der Stream nicht geöffnet, so wird FALSE zurückgegeben.    *
********************************************************************************
}

function Stream_Schliessen (var f:TFileStream): boolean;
begin
  result:= TRUE;
  try
    f.Free; // FileStream schliessen
  except
    result:= FALSE;
  end;
end;
{
********************************************************************************
* Stream_MW_Rec_Anhaengen(): Mit dieser Function wird der Datensatz am Ende    *
*                            des Stream angehängt.                            *
**-----------------------------------------------------------------------------*
* Eingabe: var f: TFileStream                                                  *
*              TMW_Stream_Rec                                                  *
* FunctionsWert: TRUE = Alles O.K. der Datensatz ist in den Stream gespeichert *
*                      worden.                                                *
*                FALSE = Es ist ein Fehler beim Speichern aufgetreten.        *
* Beschreibung: Mit dieser Function wird der Datensatz am Ende des Streams    *
*              angehängt.                                                    *
*              War der Stream nicht geöffnet, so wird FALSE zurückgegeben.    *
********************************************************************************
}

function Stream_MW_Rec_Anhaengen
                           (var f: TFileStream; MWRec: TMW_Stream_Rec): boolean;
begin
  try
    f.Position:=0;
    f.Seek(0, soFromEnd); // Datenzeiger auf das Ende des Streams setzen.
    f.WriteBuffer(MWRec, SizeOf(TMW_Stream_Rec));
    result:= TRUE;
  except
    ShowMessage('Es ist beim Datensatz speichern ein Fehler aufgetreten!');
    result:= FALSE;
  end;
end;
{
********************************************************************************
* Stream_MW_Rec_Aendern(): Mit dieser Function werden geänderte Daten in den  *
*                          Stream zurückgeschrieben                            *
**-----------------------------------------------------------------------------*
* Eingabe: var f: TFileStream                                                  *
*              MWRec: TMW_Stream_Rec  // Datensatz                            *
*              DatensatzNr: integer  // Das ist die Datensatz-Nr die zurückge-*
*                                        schrieben wird.                      *
* FunctionsWert: TRUE = Alles O.K. der Datensatz ist in den Stream gespeichert *
*                      worden.                                                *
*                FALSE = Es ist ein Fehler beim Speichern aufgetreten.        *
* Beschreibung: Mit dieser Function wird der geänderte Datensatz in dem Stream *
*              zurückgeschrieben                                              *
*              War der Stream nicht geöffnet, so wird FALSE zurückgegeben.    *
********************************************************************************
}

function Stream_MW_Rec_Aendern (var f: TFileStream; MWRec: TMW_Stream_Rec;
                                DatenSatzNr: integer): boolean;
begin
  try
    f.Position:=0;
    f.Seek((SizeOF(TMW_Stream_Rec)*DatenSatzNr), soFromBeginning);
    f.WriteBuffer(MWRec, SizeOf(TMW_Stream_Rec));
    result:= TRUE;
  except
    ShowMessage('Es ist beim Datensatz zurückschreiben in den Stream'+
                ' ein Fehler aufgetreten!');
    result:= FALSE;
  end;
end;
{
********************************************************************************
* Stream_MW_Rec_Lesen(): Mit dieser Function wird der Datensatz aus dem        *
*                        Stream gelesen und in den Record gespeichert          *
**-----------------------------------------------------------------------------*
* Eingabe: var f: TFileStream                                                  *
*          var MWRec: TMW_Stream_Rec                                          *
*              DatensatzNr: integer;                                          *
* FunctionsWert: TRUE = Alles O.K. der Datensatz ist aus dem Stream gelesen    *
*                      worden.                                                *
*                FALSE = Es ist ein Fehler beim Lesen aufgetreten.            *
* Beschreibung: Mit dieser Function wird der Datensatz mit der DatensatzNr    *
*              angegeben worden ist, in den TMW_Stream_Rec gelesen.          *
*              Der Datensatz-Zeiger wird auf den Anfang des Streams gesetz    *
*              (soFromBeginning).                                            *
*              Mit der Anweisungen: SitzeOf(TMW_Stream_Rec)*DatensatzNr      *
*              wird die Byte-Anzahl ermittelt, die der Datensatz-Zeiger      *
*              weiter bewegt werden soll.                                    *
********************************************************************************
}

function Stream_MW_Rec_Lesen (var f: TFileStream; var MWRec: TMW_Stream_Rec;
                              DatensatzNr: integer): boolean;
begin
  try
    f.Position:=0;
    // An dieser Stelle wird der Datei-Zeiger auf den zu lesenden
    // Datensatz gestellt. Durch -soFromBeginning- wird der Dateizeiger
    // auf den Anfang gesellt.
    f.Seek((SizeOf(TMW_Stream_Rec)*DatensatzNr), soFromBeginning);
    f.ReadBuffer(MWRec, SizeOf(TMW_Stream_Rec));
    result:= TRUE;
  except
    ShowMessage('Es ist beim Datensatz lesen ein Fehler aufgetreten!');
    result:= FALSE;
  end;
end;
{
********************************************************************************
* Stream_MW_Rec_DatenSatzAnzahl(): Mit dieser Function wird die Anzahl        *
*                                  der Datensatze im Stream bestimmt.          *
**----------------------------------------------------------------------------**
* Eingabe: var f: TFileStream                                                  *
*              MWRec: TMW_Stream_Rec                                          *
* FunctionsWert: Anzahl der Datensätze die im Stream vorhanden sind.          *
* Beschreibung: Mit f.Sitze wird die gesamte Anzahl der Bytes im Stream be-    *
*              stimmt. Die Gesamtzahl Bytes / Anzahl Bytes im Record ergeben  *
*              die Anzahl der Datensätze im Stream.                          *
*              dieser Function wird der Datensatz mit der DatensatzNr        *
********************************************************************************
}

function Stream_MW_Rec_DatenSatzAnzahl
                   (var f: TFileStream; MWRec: TMW_Stream_Rec): integer;
begin
  result:= 0;
  if f.Size>0 then
  begin
    // Berechnung: Gesamte Stream-Lämge / TMW_Stream_Rec
    // Das ergibt die Datensatz-Anzahl
    result:= trunc(f.Size/SizeOf(TMW_Stream_Rec));
  end;
end;


initialization


end.

An dieser Stelle nun kommt die HauptprogammUnit1

Es ist von der Benutzerführung sehr einfach gehalten und nicht gerade komfortabel, aber soll ja nur die TFileStream – Methoden in der Anwendung zeigen.

1. Datensätze eingeben:
Wobei nur die Maschinen – Nr., Messstellen - Nr. und der MW
eingegeben werden muss.
Das Datum und die Uhrzeit wird zum Zeitpunkt des Abspeicherns von der System – Zeit genommen und die beiden Flags werden dabei auf FALSE gesetzt.

2. Alle Datensätze anzeigen:
Hier werden alle Datensätze in einem StringGrid angezeigt

3. Datensatz suchen:
Hier wird nur nach den Maschinen – Nr. gesucht und zwar der erste Datensatz der die gleiche Maschine – Nr. besitzt wie der gesuchte.

4. Datensatz ändern:
Es wird dafür die Maschinen – Nr. in das Suchfeld eingetragen und anschließend auf den Such-Button gedrückt. Wenn der Gesuchte Datensatz angezeigt wird können die Maschinen – Nr. und die Messstelle – Nr. geändert werden. Darauf hin wird der Änderungs-Button gedrückt und der geänderte Datensatz wird im FileStream abgespeicher.

So das sind die grundlegenden Sachen, der Rest ist im Quell-Code dokumentiert.

Delphi-Quellcode:
unit HauptprogrammUnit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Grids, ComCtrls, Const_DatUnit, StreamUnit, ExtCtrls;

type
  TForm1 = class(TForm)
    PageControl1: TPageControl;
    TabSheet1: TTabSheet;
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    Edit1: TEdit;
    Edit2: TEdit;
    Edit3: TEdit;
    Edit4: TEdit;
    DatSatzSpeichernButton: TButton;
    TabSheet2: TTabSheet;
    StringGrid1: TStringGrid;
    Button2: TButton;
    TabSheet3: TTabSheet;
    Label6: TLabel;
    Button3: TButton;
    MaschinenNr_Suchen_Edit6: TEdit;
    Panel1: TPanel;
    Label8: TLabel;
    Label9: TLabel;
    Label10: TLabel;
    Label11: TLabel;
    Label12: TLabel;
    Label13: TLabel;
    Edit7: TEdit;
    Edit8: TEdit;
    Edit9: TEdit;
    Edit10: TEdit;
    Edit11: TEdit;
    Button4: TButton;
    StatusBar1: TStatusBar;
    Label7: TLabel;
    Edit12: TEdit;
    Label14: TLabel;
    Edit13: TEdit;
    Label15: TLabel;
    Label16: TLabel;
    Label17: TLabel;
    Edit6: TEdit;
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure DatSatzSpeichernButtonClick(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
    MW1: TMW_Stream_Rec;
    intDatSatz: integer;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button2Click(Sender: TObject);
var
  f: TFileStream;
  intDatensatzNr: integer;
  intDatensatzAnzahl: integer;
begin
// Alle Datensatz anzeigen
  // FileStream öffnen
  if not Stream_oeffnen (f, Dateiname) then
  begin
     ShowMessage('Die Datei '+ DateiName + ' ist neu angelegt worden!');
  end;
  // Anzahl Datensätz ermittlen
  intDatensatzAnzahl:= Stream_MW_Rec_DatenSatzAnzahl (f, MW1);

  intDatensatzNr:= 0;
  for intDatenSatzNr := 0 to intDatensatzAnzahl - 1 do
  begin
    Stream_MW_Rec_Lesen (f, MW1, intDatensatzNr);
    with StringGrid1 do
    begin
      Cells[0,1+intDatensatzNr]:= inttostr(intDatensatzNr+1);
      Cells[1,1+intDatensatzNr]:= MW1.stfMaNr;
      Cells[2,1+intDatensatzNr]:= MW1.stfMs;
      Cells[3,1+intDatensatzNr]:= floattostr(MW1.dblMw);
      Cells[4,1+intDatensatzNr]:= DateTimetostr(MW1.dtDatumUhrzeit);
    end;
  end;
  Stream_Schliessen (f);
end;

procedure TForm1.Button3Click(Sender: TObject);
var
  f: TFileStream;
  intDatensatzNr: integer;
  intDatensatzAnzahl: integer;
  strMaschinenNr: string; // Gesuchte Maschinen-Nr.
begin
   if not Stream_oeffnen (f, Dateiname) then
  begin
     ShowMessage('Die Datei '+ DateiName + ' ist neu angelegt worden!');
  end;
  // Anzahl Datensätz ermittlen
  intDatensatzAnzahl:= Stream_MW_Rec_DatenSatzAnzahl (f, MW1);
  // Gesuchte Maschinen-Nr eingeben
  strMaschinenNr:= MaschinenNr_Suchen_Edit6.Text;

  intDatensatzNr:= 0; // DatensatzNr auf 0 setzen.

  // Es werden die Datensätz solange durchlaufen bis eine Übereinstimmung
  // gefunden worden ist.
  for intDatenSatzNr := 0 to intDatensatzAnzahl - 1 do
  begin
    Stream_MW_Rec_Lesen (f, MW1, intDatensatzNr);
    if MW1.stfMaNr = strMaschinenNr then
    begin
      with StringGrid1 do
      begin
        Label15.Caption:= inttostr(intDatensatzNr+1);
        intDatSatz:= intDatensatzNr; // Merken welcher Datensatz
        Edit7.Text := MW1.stfMaNr;
        Edit8.Text := MW1.stfMs;
        Edit9.Text := floattostr(MW1.dblMw);
        Edit6.Text := dateTimetostr(MW1.dtDatumUhrzeit);
        Edit10.Text := booltostr(MW1.blnMWGelesen);
        Edit11.Text := booltostr(MW1.blnMWFlag);
        break; // Datensatz ist gefunden die Schleife wird abgebrochen
      end;
    end;
  end;
  Stream_Schliessen (f);
end;

procedure TForm1.Button4Click(Sender: TObject);
var
  f: TFileStream;
  intDatensatzNr: integer;
  intDatensatzAnzahl: integer;
begin
  // Nur die Maschinen-Nr. und die Meßstelle können geändert werden.
  // Die anderen Daten im Datensatz sind natürlich auch möglich.
  MW1.stfMaNr:= Edit7.Text;
  MW1.stfMs:= Edit8.Text;
  if not Stream_oeffnen (f, Dateiname) then
  begin
     ShowMessage('Die Datei '+ DateiName + ' ist neu angelegt worden!');
  end;
  // Anzahl Datensätz ermittlen
  intDatensatzAnzahl:= Stream_MW_Rec_DatenSatzAnzahl (f, MW1);
  // Gesuchte Maschinen-Nr eingeben

  Stream_MW_Rec_Aendern (f, MW1, intDatSatz);

  Stream_Schliessen (f);
end;

procedure TForm1.DatSatzSpeichernButtonClick(Sender: TObject);
var
  f: TFilestream;
begin
// Datensatz Eingeben
  MW1.stfMaNr:= Edit1.Text;
  MW1.stfMs:= Edit2.Text;
  MW1.dblMw:= strtofloat(Edit3.text);
  MW1.dtDatumUhrzeit:= now();
  Edit4.Text:= dateTimetostr (MW1.dtDatumUhrzeit);

  MW1.blnMWGelesen:= FALSE;
  MW1.blnMWFlag:= FALSE;
  Edit12.Text := booltostr(MW1.blnMWGelesen);
  Edit13.Text := booltostr(MW1.blnMWFlag);

  // FileStream öffnen
  if not Stream_oeffnen (f, Dateiname) then
  begin
     ShowMessage('Die Datei '+ DateiName + ' ist neu angelegt worden!');
  end;
  // Datensatz - Record an die Datei anhängen
  Stream_MW_Rec_Anhaengen (f, MW1);

  Stream_Schliessen (f);
end;

procedure TForm1.FormCreate(Sender: TObject);


begin
  {* Prüfen ob der Ordner: c;\StreamDatenOrdner vorhanden ist, wenn nicht wird }
  {* er auf dem c:\ laufwerk angelegt.                                         }
  Ordner_Anlegen(ORDNER_PFAD);
  DateiName:= STREAM_PFAD;
end;

end.




Ich hoffe, dass Ihr damit einigermaßen zu Recht kommt.





Mit freundlichen Grüßen

Chemiker
wer gesund ist hat 1000 wünsche wer krank ist nur einen.
  Mit Zitat antworten Zitat