Delphi-PRAXiS
Seite 1 von 3  1 23      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi Einfaches Abspeichern in externer Datei // Schülerverwaltung (https://www.delphipraxis.net/140636-einfaches-abspeichern-externer-datei-schuelerverwaltung.html)

jawo3 22. Sep 2009 20:44


Einfaches Abspeichern in externer Datei // Schülerverwaltung
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo,
ich bin gerade dabei ein Programm zu schreiben, mit dessen Hilfe ich Schülerdaten (Schülernr., Name, Vorname, Klasse, usw.) in einer Datei abspeichern kann, sodass diese nach Schließen und erneutem Öffnen des Programms über den Klick eines Buttons wieder eingelesen werden und wieder verfügbar sind. Soweit so gut. Allerdings werden, wenn ich Daten zu verschiedenen Schülern gespeichert habe, und mich vergewissert habe, dass diese auch wieder über die Schülernummer aufrufbar sind, diese nicht geladen bzw. wieder korrekt ausgegeben, wenn ich das Programm neustarte. Ich würde mich sehr freuen, wenn ihr mich ein bisschen bei der Fehlersuche unterstützen könntet. Im Unterricht wurden uns jedenfalls heute einfach diese ganzen assignfile-Sachen an den Kopf geknallt. Der Test auf IO-Fehler ist übrigens ein Pflichtelement des Programms.

Hier mal der Quelltext:
Delphi-Quellcode:
unit zeugnis;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    ButtonSpeichern: TButton;
    ButtonAuslesen: TButton;
    ButtonBeenden: TButton;
    EditSNr: TEdit;
    EditName: TEdit;
    EditVorname: TEdit;
    EditGebDat: TEdit;
    EditGebOrt: TEdit;
    EditOrt: TEdit;
    EditKlasse: TEdit;
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    Label5: TLabel;
    Label6: TLabel;
    Label7: TLabel;
    Label8: TLabel;
    Label9: TLabel;
    ButtonClear: TButton;
    EditDatensatzNr: TEdit;
    Label10: TLabel;
    ButtonSchuelerDaten: TButton;
    Label11: TLabel;
    ButtonEintragSpeichern: TButton;
    procedure ButtonBeendenClick(Sender: TObject);
    procedure ButtonSpeichernClick(Sender: TObject);
    procedure ButtonAuslesenClick(Sender: TObject);
    procedure ButtonClearClick(Sender: TObject);
    procedure ButtonSchuelerDatenClick(Sender: TObject);
    procedure ButtonEintragSpeichernClick(Sender: TObject);
  end;

  tSNr = string[5];
  tName = string[20];
  tVorname = string[15];
  tGebDat = string[10];
  tGebOrt = string[15];
  tOrt = string[15];
  tKlasse = string[4];

  tSchueler = record
        Nr             :tSNr;         //Schuelernummer bzw. Datensatznummer
        Name           :tName;        //Schuelername
        Vorname        :tVorname;     //Schuelervorname
        Geburtsort     :tGebOrt;      //Geburtsort
        GebDat         :tGebDat;      //Geburtsdatum
        Ort            :tOrt;         //Wohnort
        Klasse         :tKlasse;      //Klasse
  end;
  const Datenpfad='Schueler.dta';

var
  Form1: TForm1;
  Datensatz: array[1..99999] of tSchueler; //Array für Schuelerdatensätze
  num: integer;                            //Datensatznummer
  f: file of tSchueler;

implementation

{$R *.dfm}

procedure Kontrolle;       //Prozedur zum Test, ob einige der bekannten IO-Fehler gefunden werden
begin

 case IOResult of
 2: begin showmessage ('Datei nicht vorhanden.' +#13+ 'Datei wird angelegt');
     rewrite(f);
     closeFile(f);
    end;
 3: showmessage ('Ungueltiger Dateiname/Pfad');
 5: showmessage ('Dateizugriff verweigert.');
 21: showmessage ('Laufwerk nicht bereit!');
 end;
end;

procedure TForm1.ButtonSpeichernClick(Sender: TObject);        //Datenbank abspeichern
var i:integer;
begin
Assignfile (f,Datenpfad);
{$I-} reset (f); {$T+}
Kontrolle;
if IOResult=0 then
  begin
    for i:=1 to 99999 do write (f, Datensatz[i]);
    closefile (f);
  end;
end;

procedure TForm1.ButtonAuslesenClick(Sender: TObject);  //Datenbank laden
var i:integer;
begin
Assignfile (f,Datenpfad);
Kontrolle;
if IOResult=0 then
  begin
    for i:=1 to 99999 do read (f, Datensatz[i]);
    closefile (f);
  end;
end;

procedure TForm1.ButtonEintragSpeichernClick(Sender: TObject);           //Schüler-Eintrag speichern
var fehler: boolean; //Wird true gesetzt, wenn ein Eingabefeld leer ist
begin
fehler:=false;
num:=1;
if length(EditSNr.Text)=0 then fehler:=true
else
begin
  num:=StrToInt(EditSNr.Text);
  if length(EditSNr.Text)<>0 then Datensatz[num].Nr:= EditSNr.Text else fehler:=true;
  if length(EditName.Text)<>0 then Datensatz[num].Name:= EditName.Text else fehler:=true;
  if length(EditVorname.Text)<>0 then Datensatz[num].Vorname:= EditVorname.Text else fehler:=true;
  if length(EditGebOrt.Text)<>0 then Datensatz[num].Geburtsort:= EditGebOrt.Text else fehler:=true;
  if length(EditGebDat.Text)<>0 then Datensatz[num].GebDat:= EditGebDat.Text else fehler:=true;
  if length(EditOrt.Text)<>0 then Datensatz[num].Ort:= EditOrt.Text else fehler:=true;
  if length(EditKlasse.Text)<>0 then Datensatz[num].Klasse:= EditKlasse.Text else fehler:=true;
end;
if fehler=true then showmessage('Bitte füllen Sie alle Felder aus!')
else
  begin
    showmessage('Eintrag erfolgreich gespeichert');
  end;
end;

procedure TForm1.ButtonClearClick(Sender: TObject);    //Felder leeren
begin
EditSNr.Text:='';
EditName.Text:='';
EditVorname.Text:='';
EditGebOrt.Text:='';
EditGebDat.Text:='';
EditOrt.Text:='';
EditKlasse.Text:='';
end;

procedure TForm1.ButtonSchuelerDatenClick(Sender: TObject);          //Daten zu einem Schüler anzeigen
var i: integer;            //Datensatznummer
begin
i:=StrToInt(EditDatensatzNr.Text);
if (length(EditDatensatzNr.Text)<>0) and (length(EditDatensatzNr.Text)<=5) then
begin
  EditSNr.Text:=Datensatz[i].Nr;
  EditName.Text:=Datensatz[i].Name;
  EditVorname.Text:=Datensatz[i].Vorname;
  EditGebOrt.Text:=Datensatz[i].GeburtsOrt;
  EditGebDat.Text:=Datensatz[i].GebDat;
  EditOrt.Text:=Datensatz[i].Ort;
  EditKlasse.Text:=Datensatz[i].Klasse;
end
else showmessage('Bitte eine gültige Datensatz-Nummer eintragen');
end;

procedure TForm1.ButtonBeendenClick(Sender: TObject);           //Programm beenden
begin
close;
end;
end.
Im Anhang habe ich noch mal ein Bild, das zeigt, wie das Programm so aussieht.


Vielen Dank
Jan

Klaus01 22. Sep 2009 21:21

Re: Einfaches Abspeichern in externer Datei // Schülerverwal
 
Guten Abend,

ich würde die Prozedure Kontrolle zu einer Funktion umbauen:

Delphi-Quellcode:
function Kontrolle:Boolean;       //Prozedur zum Test, ob einige der bekannten IO-Fehler gefunden werden
begin
  result := false;
  case IOResult of
     0: result := true;
     2: begin showmessage ('Datei nicht vorhanden.' +#13+ 'Datei wird angelegt');
          rewrite(f);
          // closeFile(f); // wenn Du die Datei erstellt hast nicht gleich wieder schließen Du willst ja noch etwas abspeichern
          result := true;
        end;
     3: showmessage ('Ungueltiger Dateiname/Pfad');
     5: showmessage ('Dateizugriff verweigert.');
    21: showmessage ('Laufwerk nicht bereit!');
  end;
end;
und sie dann so einsetzen:
Delphi-Quellcode:
procedure TForm1.ButtonSpeichernClick(Sender: TObject);        //Datenbank abspeichern
var i:integer;
begin
Assignfile (f,Datenpfad);
{$I-} reset (f); {$I+}
if Kontrolle then
  begin
    for i:=1 to 99999 do write (f, Datensatz[i]);
    closefile (f);
  end;
end;

denn in Deinem Konstrukt wird IOResult immer 0 sein.
Denn wenn IOResult einmal abgefragt wurde wird IOError gelöscht:

Aus der Hilfe:
Zitat:

IOResult returns the result of the last Delphi I/O routine when I/O-checking is off (that is, when using the {$I-} compiler option). If IOResult returns 0, the last I/O operation succeeded. Otherwise, IOResult returns an error code.

If an I/O error occurs and I/O-checking is off, all subsequent I/O operations are ignored until the internal error flag is cleared. Calling IOResult clears the internal error flag.
Ob das Dein Problem behebt habe ich nicht überprüft.

[edit]
Delphi-Quellcode:
const Datenpfad='Schueler.dta';
hier vielleicht einen absoluten Pfad zur Datei angeben.

[edit2]
Delphi-Quellcode:
procedure TForm1.ButtonAuslesenClick(Sender: TObject);  //Datenbank laden
var i:integer;
begin
  i:=1;
  Assignfile (f,Datenpfad);
  {$I-} reset(f) {$I+}
  if Kontrolle then
    begin
       while not eof(f) do
         begin
           read(f, Datensatz[i]);
           inc(i);
         end;
     end;
   closefile (f);
  end;
end;

Grüße
Klaus

taaktaak 23. Sep 2009 07:17

Re: Einfaches Abspeichern in externer Datei // Schülerverwal
 
Moin, Moin.
  • Die Funktion "Kontrolle" sollte m. E. besser nur eine Funktionalität haben und nicht so nebenbei auch noch an den Dateivariablen "herummachen". Also auf die Ausgabe einer Meldung beschränken und dann auch gleich einen anderen (passenden) Namen vergeben.
  • Da die Datei immer komplett eingelesen und geschrieben wird, sollte die beiden Prozeduren m. E. wie folgt beginnen:
Delphi-Quellcode:
procedure TForm1.ButtonSpeichernClick(Sender:TObject);
var i : Integer;
    f : file of tSchueler;
begin
  assignfile(f,Datenpfad);
  {$I-} rewrite (f); {$I+}
  ..

procedure TForm1.ButtonLadenClick(Sender:TObject);  
var i : Integer;
    f : file of tSchueler;
begin
  assignfile(f,Datenpfad);
  {$I-} reset (f); {$I+}
  ..
PS:
a) So wenig wie möglich global deklarieren!
b) "Datensatz: array[1..99999] of tSchueler;" ist schon heftig! Kennt ihr noch keine dynamischen Arrays?

hoika 23. Sep 2009 08:41

Re: Einfaches Abspeichern in externer Datei // Schülerverwal
 
Hallo,

statt

Delphi-Quellcode:
tSchueler = record
würde ich

Delphi-Quellcode:
tSchueler = packed record
schreiben und die alte Datei löschen..

Ausserdem kannst du mit FileExists prüfen,
ob die Datei vorhanden ist und dann entweder Reset oder ReWrite benutzen.

Das ersetzt natürlich das IOResult nicht.

Bei
procedure TForm1.ButtonEintragSpeichernClick

fiel mir noch was auf.

Was passiert, wenn der letzte Editor leer ist ...
Du hast alle anderen Werte schon in den Datensatz eingetragen
und ereugst zum Schluss einen Fehler.
Die anderen Werte stehen aber trotzdem schon drin !

Besser wäre hier

Delphi-Quellcode:
fehler:= (EditName.Text='') or
         (EditOrt.Text='') or

             usw.

         (EditOrt.EditKlasse='');

Heiko

p80286 23. Sep 2009 10:55

Re: Einfaches Abspeichern in externer Datei // Schülerverwal
 
Hallo Jan,

die wichtigste Antwort hat Dir wohl Klaus schon gegeben:
Delphi-Quellcode:
while not eof(f) do
         begin
           read(f, Datensatz[i]);
           inc(i);
         end;
Bei Deiner Lösung (for i:=0 to 99999....) hast immer das Problem, daß eine teilweise beschädigte Datei das Lesen unmöglich macht.

Mir behagt die Definition des records nicht
Delphi-Quellcode:
  tSNr = string[5];
  tName = string[20];
  tVorname = string[15];
  tGebDat = string[10];
  tGebOrt = string[15];
  tOrt = string[15];
  tKlasse = string[4];

  tSchueler = record
        Nr             :tSNr;         //Schuelernummer bzw. Datensatznummer
        Name           :tName;        //Schuelername
        Vorname        :tVorname;     //Schuelervorname
        Geburtsort     :tGebOrt;      //Geburtsort
        GebDat         :tGebDat;      //Geburtsdatum
        Ort            :tOrt;         //Wohnort
        Klasse         :tKlasse;      //Klasse
  end;
Ich würde mit fixen Satzlängen arbeiten:

Delphi-Quellcode:
  tSNr =    Array of Char[1..5];
  tName =   Array of Char[1..20];
  tVorname = Array of Char[1..15];
  tGebDat = Array of Char[1..10];
  tGebOrt = Array of Char[1..15];
  tOrt =    Array of Char[1..15];
  tKlasse = Array of Char[1..4];

  tSchueler = record
        Nr             :tSNr;         //Schuelernummer bzw. Datensatznummer
        Name           :tName;        //Schuelername
        Vorname        :tVorname;     //Schuelervorname
        Geburtsort     :tGebOrt;      //Geburtsort
        GebDat         :tGebDat;      //Geburtsdatum
        Ort            :tOrt;         //Wohnort
        Klasse         :tKlasse;      //Klasse
  end;
Wobei allerdings ein leerer Datensatz und ein mit Leerzeichen gefüllter Datensatz nicht zu unterscheiden wären.
(Ok da kann man noch eine #0 als Begrenzer einsetzen, verliert aber ein Datenzeichen)

Wenn Du dann noch ein #13#10 als Abschluß Deines Records hinein packst, ist die Datei problemlos mit jedem Editor les- und kontrollierbar.

Gruß
K-H

himitsu 23. Sep 2009 11:13

Re: Einfaches Abspeichern in externer Datei // Schülerverwal
 
@p80286: das sind ShortStrings und die haben immer eine fixe Größe :zwinker:

String[8] ist ein AnsiString mit maximal 8 Zeichen Inhalt,
die Größe im Speicher liegt immer bei genau 9 Byte
und im Gegensatz zu den "normalen" Strings gibt es hier keinen internen Pointer.

p80286 23. Sep 2009 13:18

Re: Einfaches Abspeichern in externer Datei // Schülerverwal
 
@himitsu

Pardon ungenau ausgedrückt.
wenn Du z.B. "Ralf" abspeichern willst, dann steht hinterher in der Datei
#4Ralfxxxxxxxxxxxxxx
wobei jedes x für irgendein beliebiges Zeichen steht.
Das ist bei meinem Vorschlag nicht der Fall, da ja jedes Feld vorher initialisiert wird/werden sollte.(Hab ich auch vergessen zu schreiben)

Das es ein paar intelligentere Formate zum Abspeichern gibt, lassen wir mal aussen vor.

Gruß
K-H

spaxxn 23. Sep 2009 13:59

Re: Einfaches Abspeichern in externer Datei // Schülerverwal
 
Hihi, genau diese Aufgabe hatte ich am Anfang meines Studiums auch auf dem Tisch liegen.

Statt deines Arrays würde ich eine doppelt verkettete Liste verwenden und beim Auslesen auf EOF prüfen. Die hat den Vorteil, dass du nicht gebunden bist.

Aber ist Geschmackssache.

himitsu 23. Sep 2009 14:25

Re: Einfaches Abspeichern in externer Datei // Schülerverwal
 
Also einen ShortString kann man auch initialisieren, wenn man denn will.

jawo3 28. Sep 2009 20:39

Re: Einfaches Abspeichern in externer Datei
 
Sorry, dass ich mich nicht gemeldet habe.

Also ich habe mal versucht eure Vorschläge mit einzuarbeiten. Wenn ich jetzt allerdings auf das Feld "Datenbank Laden" klicke erhalte ich eine Fehlermeldung in neuem Fenster: "Project raised exception class EInOutError with message 'Read beyond end of file'. Process stopped. Use step or run to continue"

So sieht mein Quelltext jetzt aus:

Delphi-Quellcode:
unit zeugnis;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    ButtonSpeichern: TButton;
    ButtonAuslesen: TButton;
    ButtonBeenden: TButton;
    EditSNr: TEdit;
    EditName: TEdit;
    EditVorname: TEdit;
    EditGebDat: TEdit;
    EditGebOrt: TEdit;
    EditOrt: TEdit;
    EditKlasse: TEdit;
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    Label5: TLabel;
    Label6: TLabel;
    Label7: TLabel;
    Label8: TLabel;
    Label9: TLabel;
    ButtonClear: TButton;
    EditDatensatzNr: TEdit;
    Label10: TLabel;
    ButtonSchuelerDaten: TButton;
    Label11: TLabel;
    ButtonEintragSpeichern: TButton;
    procedure ButtonBeendenClick(Sender: TObject);
    procedure ButtonSpeichernClick(Sender: TObject);
    procedure ButtonAuslesenClick(Sender: TObject);
    procedure ButtonClearClick(Sender: TObject);
    procedure ButtonSchuelerDatenClick(Sender: TObject);
    procedure ButtonEintragSpeichernClick(Sender: TObject);
  end;

  tSNr = string[5];
  tName = string[20];
  tVorname = string[15];
  tGebDat = string[10];
  tGebOrt = string[15];
  tOrt = string[15];
  tKlasse = string[4];

  tSchueler = record
        Nr             :tSNr;         //Schuelernummer bzw. Datensatznummer
        Name           :tName;        //Schuelername
        Vorname        :tVorname;     //Schuelervorname
        Geburtsort     :tGebOrt;      //Geburtsort
        GebDat         :tGebDat;      //Geburtsdatum
        Ort            :tOrt;         //Wohnort
        Klasse         :tKlasse;      //Klasse
  end;
  const Datenpfad='Schueler.dta';

var
  Form1: TForm1;
  Datensatz: array[1..10] of tSchueler; //Array für Schuelerdatensätze
  f: file of tSchueler;

implementation

{$R *.dfm}

function Kontrolle:Boolean;       //Prozedur zum Test, ob einige der bekannten IO-Fehler gefunden werden
begin
  result := false;
  case IOResult of
     0: result := true;
     2: begin showmessage ('Datei nicht vorhanden.' +#13+ 'Datei wird angelegt');
          rewrite(f);
          result := true;
        end;
     3: showmessage ('Ungueltiger Dateiname/Pfad');
     5: showmessage ('Dateizugriff verweigert.');
    21: showmessage ('Laufwerk nicht bereit!');
  end;
end;

procedure TForm1.ButtonSpeichernClick(Sender: TObject);        //Datenbank abspeichern
var i:integer;
begin
Assignfile (f,Datenpfad);
{$I-} reset (f); {$I+}
if Kontrolle then
  begin
    for i:=1 to 10 do write (f, Datensatz[i]);
    closefile (f);
  end;
end;

procedure TForm1.ButtonAuslesenClick(Sender: TObject);  //Datenbank laden
var i:integer;
begin
  i:=1;
  Assignfile (f,Datenpfad);
  {$I-} reset(f); {$I+}
  if Kontrolle then
    begin
       while not eof(f) do
         begin
           read(f, Datensatz[i]);
           inc(i);
         end;
     end;
   closefile (f);
end;

procedure TForm1.ButtonEintragSpeichernClick(Sender: TObject);    //Schüler-Eintrag speichern
var fehler: boolean;                                              //Wird true gesetzt, wenn ein Eingabefeld leer ist
         i:integer;
begin
fehler:=false;
i:=1;
if length(EditSNr.Text)=0 then fehler:=true
else
begin
  i:=StrToInt(EditSNr.Text);
  if length(EditSNr.Text)<>0 then Datensatz[i].Nr:= EditSNr.Text else fehler:=true;
  if length(EditName.Text)<>0 then Datensatz[i].Name:= EditName.Text else fehler:=true;
  if length(EditVorname.Text)<>0 then Datensatz[i].Vorname:= EditVorname.Text else fehler:=true;
  if length(EditGebOrt.Text)<>0 then Datensatz[i].Geburtsort:= EditGebOrt.Text else fehler:=true;
  if length(EditGebDat.Text)<>0 then Datensatz[i].GebDat:= EditGebDat.Text else fehler:=true;
  if length(EditOrt.Text)<>0 then Datensatz[i].Ort:= EditOrt.Text else fehler:=true;
  if length(EditKlasse.Text)<>0 then Datensatz[i].Klasse:= EditKlasse.Text else fehler:=true;
end;
if fehler=true then showmessage('Bitte füllen Sie alle Felder aus!')
else
  begin
    showmessage('Eintrag erfolgreich gespeichert');
  end;
end;

procedure TForm1.ButtonClearClick(Sender: TObject);    //Felder leeren
begin
EditSNr.Text:='';
EditName.Text:='';
EditVorname.Text:='';
EditGebOrt.Text:='';
EditGebDat.Text:='';
EditOrt.Text:='';
EditKlasse.Text:='';
end;

procedure TForm1.ButtonSchuelerDatenClick(Sender: TObject);          //Daten zu einem Schüler anzeigen
var i: integer;            //Datensatznummer
begin
i:=StrToInt(EditDatensatzNr.Text);
if (length(EditDatensatzNr.Text)<>0) and (length(EditDatensatzNr.Text)<=5) then
begin
  EditSNr.Text:=Datensatz[i].Nr;
  EditName.Text:=Datensatz[i].Name;
  EditVorname.Text:=Datensatz[i].Vorname;
  EditGebOrt.Text:=Datensatz[i].GeburtsOrt;
  EditGebDat.Text:=Datensatz[i].GebDat;
  EditOrt.Text:=Datensatz[i].Ort;
  EditKlasse.Text:=Datensatz[i].Klasse;
end
else showmessage('Bitte eine gültige Datensatz-Nummer eintragen');
end;

procedure TForm1.ButtonBeendenClick(Sender: TObject);           //Programm beenden
begin
close;
end;
end.
Ich habe noch nicht ganz verstanden, wie ihr das mit den dynamische Arrays meint. Ich habe jetzt erstaml aus Testzwecken die Schülerzahl auf 10 gesenkt.


Vielen Dank im Voraus
Jan


Alle Zeitangaben in WEZ +1. Es ist jetzt 12:02 Uhr.
Seite 1 von 3  1 23      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz