Announcement

Collapse
No announcement yet.

Variante Records ansprechen und speichern

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Variante Records ansprechen und speichern

    Moin ;o) <BR>
    Ich würde gerne eine Save Datei für mein Programm erstellen das eine Art Datenbank ähnelt. Der erste Inhalt sollte eine in einer Art Header/ Dateikopf für allgemeine Info's in einem record gespeichert werden und in der gleichen Datei dann weitere Record's für die eigentlichen Nutzdaten.<BR>
    Ich habe einmal von Variante Records gelesen und in der Delphi - Hilfe nachgelesen und habe mir einmal so einen TestRecord erstellt.<BR>
    <PRE>
    type
    TTestRec = Record
    case Header: boolean of
    true: (Flag : Boolean; // negativ wenn Datenbank nicht ordnungsgemäß geschlossen wurde
    MaxDS: Word; // Anzahl der Datensätze
    BName: String[20]; // V. Nachname
    BID : Cardinal); // Benutzernummer
    falseDBeg : TDateTime; // Dienstbeginn, Datum und Uhrzeit
    DP : String[5]; // Pause
    DEnd : TDateTime; // Dienstende, Datum und Uhrzeit
    DPl : Word; // Dienstplannummer
    Memo : String[50]); // Anmerkungen zur Dienstschicht
    end;
    </PRE>

    Nur bin ich genauso Dumm wie vorher! Wie kann ich diesen Record speichern und laden... Wenn jemand einen Vorschlag hätte wie ich das umsetzen könnte wäre ich sehr dankbar ...<P>

    Servus Mario

  • #2
    Hi,

    eigentlich ist das ganz einfach. Du speicherst deinen Record wie "normale" Records in einer typisierten Datei. Hier mal ein Beispiel:<BR>
    <BR>
    //Record speichern<BR>
    var<BR>
    REC : TTESTRECORD;<BR>
    DAT : File of TTestrecord;<BR>
    <BR>
    begin<BR>
    {* Hier Record befüllen *}<BR>
    Assignfile(DAT,'C:\TEST.DAT');<BR>
    REWRITE(DAT);<BR>
    write(DAT,REC);<BR>
    CloseFile(DAT);<BR>
    end;<BR>
    <BR>
    //Record lesen<BR>
    var<BR>
    REC : TTESTRECORD;<BR>
    DAT : File of TTestrecord;<BR>
    <BR>
    begin<BR>
    Assignfile(DAT,'C:\TEST.DAT');<BR>
    Reset(DAT);<BR>
    Read(DAT,REC);<BR>
    CloseFile(DAT);<BR>
    //Mach irgendwas mit den Daten<BR>
    end;<BR>
    <BR>
    Ich hoffe das hilft dir weiter.<BR>

    Uw

    Comment


    • #3
      Ich würde an deiner Stelle aus dem einen varianten Record lieber
      zwei getrennte normale Records machen und ein file of TNormalerRec
      verwenden. Den Header kannst du dann in deiner Schreib- und Leseroutine mit etwas Tricksen (Casten/Move/absolute) in die Datei "reinmogeln".
      Hier mal ein ungetestes Fragment:
      <pre>
      <code><font size=2 face="Courier New"><font color="#000000"> </font>THeaderRec = <b>record
      </b>Flag: Boolean; <font color="#008000"><i>// negativ wenn Datenbank nicht ordnungsgem&auml;&szlig; geschlossen wurde
      </i></font>MaxDS: Word; <font color="#008000"><i>// Anzahl der Datens&auml;tze
      </i></font>BName: <b>string</b>[<font color="#0000FF">20</font>]; <font color="#008000"><i>// V. Nachname
      </i></font>BID: Cardinal; <font color="#008000"><i>// Benutzernummer
      </i></font><b>end</b>;
      <br>
      TTestRec = <b>record
      </b>DBeg: TDateTime; <font color="#008000"><i>// Dienstbeginn, Datum und Uhrzeit
      </i></font>DP: <b>string</b>[<font color="#0000FF">5</font>]; <font color="#008000"><i>// Pause
      </i></font>DEnd: TDateTime; <font color="#008000"><i>// Dienstende, Datum und Uhrzeit
      </i></font>DPl: Word; <font color="#008000"><i>// Dienstplannummer
      </i></font>Memo: <b>string</b>[<font color="#0000FF">50</font>]; <font color="#008000"><i>// Anmerkungen zur Dienstschicht
      </i></font><b>end</b>;
      <br>
      </font></font>
      <font size=2 face="Courier New"><font color="#000000"><b>procedure </b>TForm1.SaveButtonClick(Sender: TObject);
      <b>var
      </b>rec: TTestRec;
      hdr: THeaderRec <b>absolute </b>rec;
      dat: <b>file of </b>TTestRec;
      <b>begin
      </b>Assert(sizeof(THeaderRec) &lt;= sizeof(TTestRec)); <font color="#008000"><i>//Anderenfalls funktioniert's nicht ganz so einfach
      </i></font>Assignfile(dat, <font color="#FF00FF">'C:\TEST.dat'</font>);
      Rewrite(dat);
      <br>
      <font color="#008000"><i>// Header bef&uuml;llen (&uuml;ber Variable &quot;hdr&quot:
      </i></font>hdr.Flag := true;
      <font color="#008000"><i>//...
      </i></font>write(dat, rec); <font color="#008000"><i>// Header wegschreiben &uuml;ber Variable &quot;rec&quot; (!)
      <br>
      </i></font><b>while </b>NochSaetzeDa <b>do
      begin
      </b><font color="#008000"><i>// Datensatz bef&uuml;llen:
      </i></font>rec.Memo := <font color="#FF00FF">'Test'</font>;
      <font color="#008000"><i>// ...
      </i></font>write(dat, rec);
      <b>end</b>;
      <br>
      CloseFile(dat);
      <b>end</b>;
      </font></font>
      </code></pre>

      Die Lösung ist vielleicht nicht ganz sauber, aber den "unsauberen" Teil hast du ja nur an zwei Stellen (beim Schreiben und beim Lesen).
      Der Vorteil ist meiner Meinung nach, dass du beim Arbeiten mit den
      normalen Datensätzen nicht mehr dauernd mit den Feldern des Headersatzes zu tun hast. (z.B. beim IntelliSense (=Programmierhilfe?))
      <br>Ach ja: Du musst bei meiner Lösung auf die Satzgrößen achten (siehe Assert im Bsp.). Wenn der Header zu groß ist, bräuchtest du u.U. mehrere "normale" Datensätze Platz am Dateianfang -- das ginge
      wahrscheinlich mit BlockWrite ganz gut.
      <br> Ciao, Uli

      Comment


      • #4
        Hallo Herr Rupprecht und Herr Gerhardt ...<BR>
        Sorry, ich habe mich sicherlich zum Anfang falsch ausgedrückt. Ich habe bisher mit 2 verschiedenen Dateien gearbeitet ( Inidatei für Header ) und möchte diese nun irgendwie zusammenspeichern. Deswegen bin ich auf die varianten Records gekommen. Die eigentliche Datendatei habe ich bisher mittels Record gespeichert. Diese Routinen hätte ich also oder müßte diese nur ein wenig modifizieren.
        <PRE>
        type
        TTestRec = Record
        DBeg : TDateTime; // Dienstbeginn, Datum und Uhrzeit
        DP : String[5]; // Pause
        DEnd : TDateTime; // Dienstende, Datum und Uhrzeit
        DPl : Word; // Dienstplannummer
        Memo : String[50]); // Anmerkungen zur Dienstschicht
        end;
        </PRE> <PRE>
        var Daten_Datei: File of TTestRec; // Dateivariable
        Bytes_Datei: File of Byte; // Dateivariable
        Daten_Rec : TTestRec; // Recordvariable

        ...
        </PRE> <P> <PRE>
        procedure TForm1.Datensatz_lesen(Satz: SmallInt);
        begin
        Reset(Daten_Datei); // Datei öffnen
        Seek(Daten_Datei, Satz); // Datensatzzeiger setzen
        Read(Daten_Datei, dataframe1.Daten_Rec); // Datensatz lesen
        CloseFile(Daten_Datei); // Datei schliessen
        end;
        </PRE> <P> <PRE>
        procedure TForm1.Datensatz_speichern(Satz: SmallInt);
        begin
        Reset(Daten_Datei); // Datei öffnen
        if Satz = -1 then
        Satz:= FileSize(Daten_Datei); // auf letzten Datensatz setzen
        Seek(Daten_Datei, Satz); // Datensatzzeiger setzen
        Write(Daten_Datei, Daten_Rec); // Datensatz schreiben
        CloseFile(Daten_Datei); // Datei schliessen
        end;
        </PRE> <P> <PRE>
        procedure TForm1.Datenbank_laden(FN: TFileName);
        var i: SmallInt;
        begin
        while FN = '' do begin // Dateiname zugewiesen ??
        if OpenDialog1.Execute then // Dialog öffnen
        FN:= OpenDialog1.FileName; // Dateiname zuweisen
        end;
        AssignFile(Daten_Datei, FN); // Dateiname mit Variable
        AssignFile(Bytes_Datei, FN); // Dateiname mit Variable
        if not FileExists(FN) then // Datei nicht vorhanden ??
        Datenbank_erstellen(FN); // ... dann erstellen !!
        Datenbank_Info;
        if Record_ges > 1 then begin // Datensätze einlesen
        showmessage(inttostr(record_ges -1)+ ' Datensätze gefunden');
        // StringGrid1.RowCount:= Record_ges;
        for i:= 1 to (Record_ges -1) do begin
        Datensatz_lesen(I);
        StringGrid1.Cells[1, I]:= Daten_Rec.Datum;
        StringGrid1.Cells[2, I]:= Daten_Rec.Dplan;
        StringGrid1.Cells[3, I]:= Daten_Rec.Schicht;
        StringGrid1.Cells[4, I]:= Daten_Rec.AZeit;
        end;
        end;
        Form1.Caption:= FN;
        end;

        </PRE>

        &#10

        Comment


        • #5
          Hallo Herr Rupprecht und Herr Gerhardt ...<BR>
          Vielen Dank ersteinmal für Ihre Mühe mei der Beantwortung. Sorry, ich habe mich sicherlich zum Anfang falsch ausgedrückt. Ich bin nicht so geübt in Delphi und habe bisher mit 2 verschiedenen Dateien gearbeitet ( Inidatei für Header ) und möchte diese nun irgendwie zusammenspeichern. Deswegen bin ich auf die varianten Records gekommen. Die eigentliche Datendatei habe ich bisher mittels Record gespeichert. Diese Routinen hätte ich also oder müßte diese nur ein wenig modifizieren.<BR>
          Bisher sah das so aus ...
          <PRE>
          type
          TTestRec = Record
          DBeg : TDateTime; // Dienstbeginn, Datum und Uhrzeit
          DP : String[5]; // Pause
          DEnd : TDateTime; // Dienstende, Datum und Uhrzeit
          DPl : Word; // Dienstplannummer
          Memo : String[50]); // Anmerkungen zur Dienstschicht
          end;
          </PRE> <PRE>
          var Daten_Datei: File of TTestRec; // Dateivariable
          Bytes_Datei: File of Byte; // Dateivariable
          Daten_Rec : TTestRec; // Recordvariable

          ...
          </PRE> <P> <PRE>
          procedure TForm1.Datensatz_lesen(Satz: SmallInt; FN: TFileName);
          begin
          AssignFile(Daten_Datei, FN); // Dateiname mit Variable
          AssignFile(Bytes_Datei, FN); // Dateiname mit Variable
          Reset(Daten_Datei); // Datei öffnen
          Seek(Daten_Datei, Satz); // Datensatzzeiger setzen
          Read(Daten_Datei, Daten_Rec); // Datensatz lesen
          CloseFile(Daten_Datei); // Datei schliessen
          end;
          </PRE> <P> <PRE>
          procedure TForm1.Datensatz_speichern(Satz: SmallInt; FN: TFileName);
          begin
          AssignFile(Daten_Datei, FN); // Dateiname mit Variable
          AssignFile(Bytes_Datei, FN); // Dateiname mit Variable
          Reset(Daten_Datei); // Datei öffnen
          if Satz = -1 then // neuen Datensatz anhängen
          Satz:= FileSize(Daten_Datei); // auf letzten Datensatz setzen
          Seek(Daten_Datei, Satz); // Datensatzzeiger setzen
          Write(Daten_Datei, Daten_Rec); // Datensatz schreiben
          CloseFile(Daten_Datei); // Datei schliessen
          end;
          </PRE>
          <P>
          ... nur wie spreche ich jetzt beim Laden und Speichern den Header oder die Datensätze speziell an? Wenn ich den Header mit "Datensatz_lesen(0)" laden möchte, muß ich dann TTestRec net so ansprechen damit er weiß welche Variablen im Record er füllen muß? <BR>
          <PRE>
          procedure TForm1.Datensatz_lesen(Satz: SmallInt);
          begin
          AssignFile(Daten_Datei, FN); // Dateiname mit Variable
          AssignFile(Bytes_Datei, FN); // Dateiname mit Variable
          if Satz = 0 then
          ... // nur was???????????????????
          Reset(Daten_Datei); // Datei öffnen
          Seek(Daten_Datei, Satz); // Datensatzzeiger setzen
          Read(Daten_Datei, Daten_Rec); // Datensatz lesen
          CloseFile(Daten_Datei); // Datei schliessen
          end;
          </PRE>
          Bin ich nur auf dem Holzweg oder ... ( <P>
          Ich hoffe es kann noch jemand etwas Licht in mein dunkel bringen<BR>
          Ciao Mari

          Comment


          • #6
            Hi,

            ok..mal ein kurzer Exkurs.

            Bei Varianten Records ermittelt der Compiler zuerstmal den größten variablen Block im Record. Dieser wird dann (egal welcher Teil befüllt ist) als Satzgröße (zur Speicherung) angenommen. Daher kann der variable Teil auch nur am Ende der Recorddefinition stehen. D.h. egal welchen Teil des Records du befüllst, der zu speichernde Block wird immer auf die max. Größe (Allgemeiner Teil + größter Variant-Teil) gespeichert.

            Ok..also ist den Header-Satz genauso groß wie der Daten-Teil.

            Wenn du also deinen Header-Satz im Satz Nr. 0 gespeichert hast, kannst du ihn genauso lesen wie einen normalen Datensatz. Der Unterschied ist die Variable Header im Record. Ist sie True werden
            die Felder des Header-Teils befüllt. Ist sie False werden die Teile des Datenteils befüllt.

            Bsp.

            Daten_SatzLesen(xyz,rec);
            if rec.header then<BR>
            <FONT color="#00ff00">//Mach was mit den Headerdaten</FONT><BR>
            else<BR>
            <FONT color="#00ff00">//Mach was mit den Datendaten.</FONT><BR>
            <BR>
            <BR>

            Ich hoffe das hilft dir weiter.

            Uw

            Comment


            • #7
              Hallo Uwe... <BR>
              Vielen Dank für Deine Ausführungen. Soweit habe ich das jetzt verstanden. Ich habe mir das sicherlich etwas schwieriger vorgestellt die varianten Teile auszulesen. Sorry wenn ich nerve, aber würde das im Umkehrschluß mit dem schreiben des Records bedeuten das ich "rec.Header" erst zuweisen muß?<BR>
              Bsp'weise: rec.Header:= true -> für den Header?<BR>
              Ich werde mir heute noch schnell ein Testproggy schreiben um das von Dir ausgeführte mal zu testen.<BR>
              ... auf jeden Fall nochmal's Danke, Gruß Mari

              Comment


              • #8
                Moin ;o) <BR>
                ... lange Nacht und doch nix gebracht. Das mit dem auslesen des Headers und der Datensätze hab ich ja gedanklich gerafft und in meinem Programm umgesetzt... nur mit dem schreiben das bekomme ich einfach net hin. Wie mache ich das, das der Record weiß welcher variante Teil in die Datenbank zu schreiben ist. Das bloße zuweisen der Variablen durch "rec.header:= true" bringt nicht den gewünschten Erfolg. Vielleicht kann mir ja jemand ein einfaches Demoprogramm schreiben damit ich das auf die reihe bekomme oder ich komme nicht umhin den Vorschlag von Ulrich umzusetzen. Wäre wirklich nett.<BR>
                Gute Nacht und hoffe auf einen besseren Tag, Ciao Mari

                Comment


                • #9
                  Hi,

                  also mal ein etwas ausführlicheres Beispiel:

                  <pre>
                  <code><font size=2 face="Courier New"><font color="#000000"><b>var
                  </b>Datei : <b>File of </b>TTestrec;
                  REC : TTestrec;

                  <b>procedure </b>WriteData;
                  <b>begin
                  </b>Seek(Datei,Filesize(datei)); <i>//Dateizeiger auf das Ende der Datei Positionieren
                  </i>write(datei,rec);
                  <b>end</b>;

                  <b>procedure </b>ReadData(SatzNr:Longint);
                  <b>begin
                  </b>Seek(datei,Satznr);
                  read(datei,rec);
                  <b>end</b>;

                  <b>procedure </b>TForm1.Button1Click(Sender: TObject);
                  <b>begin
                  </b><i>//&Ouml;ffnen der Datei
                  </i>Assignfile(Datei,'C:\TEST.DAT');
                  <b>if </b>FileExists('C:\TEST.DAT') <b>then
                  </b>RESET(Datei)
                  <b>else
                  </b>Rewrite(Datei);
                  <i>//Hier steht der sog. Dateizeiger auf dem Anfang der Datei
                  </i>rec.Header := true;
                  writeData;
                  rec.header := false;
                  writeData;
                  ReadData(0);
                  <b>if </b>(rec.header) <b>then
                  </b>ShowMessage('Headersatz')
                  <b>else
                  </b>ShowMessage('Datensatz');
                  closefile(Datei);
                  <b>end</b>;
                  </font>
                  </code></pre>

                  Anmerkung:

                  Durch Reset oder Rewrite wird der Dateizeiger immer wieder auf den Dateianfang gesetzt. Wenn du also deinen Header wegschreibst, die Datei schließt und anschließend neu aufmachst und den Datensatz schreibst, hast du nur 1. Datensatz. Der Headersatz wurde überschrieben.

                  Gruß

                  Uw

                  Comment


                  • #10
                    Hallo Uwe ..., <BR>
                    komme leider erst heute dazu Dir für die schnelle und kompetente Hilfe zu danken. Ich habe Dein Bsp. umgesetzt und siehe da, es funktioniert. In meiner Routine hatte sich ein kleiner Denkfehler eingeschlichen.<P>
                    Gruß Mari

                    Comment

                    Working...
                    X