PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Dateiarbeit für eine Hall-Of-Fame


wenzi496
27.10.2004, 19:02
hi leutz,
ich wollte mit hilfe von dateiarbeit eine hall-of-fame basteln. dabei habe ich ersteinmal versucht ein einfaches programm zu schreiben mit dem man daten eingeben, speichern, anzeigen lassen und wieder ändern kann.
mein problem ist, dass, wenn nur ein element in der liste vorhanden ist oder, wenn man das letzte element ändern will, ich einen disk read/write error bekomme.
ich weiß aber nich wieso. vielleicht fällt ja jemandem der fehler auf!

danke schonmal


Program highscore;
uses crt;
const dateiname='HoF.dat';
type dateityp=integer;
tdatei=File of Dateityp;

var Datei:tdatei;
datensatz:dateityp;

Procedure init(Var d:tdatei);
begin
assign(d,dateiname);
rewrite(d);
close(d);
end;

Function gross(Var d:tdatei;datensatz:dateityp):boolean;
var tempsatz:dateityp;
Begin
assign(d,dateiname);
reset(d);
if filesize(d) <> 0 then
begin
seek (d,filesize(d)-1);
read(d,tempsatz);
gross:=datensatz>tempsatz;
end;
close(d);
end;

Procedure save(var datei:tdatei; datensatz:dateityp);
var anz:dateityp;
begin
assign(datei,dateiname);
reset(datei);
anz:=filesize(datei);
seek(datei,anz);
write(datei,datensatz);
close(datei);
end;

Procedure zeigen(datensatz:dateityp);
begin
writeln(datensatz);
end;

Procedure load(var d:tdatei);
var datensatz:dateityp;
begin
assign(d,dateiname);
reset(d);
while not (EOF(d)) do
begin
read(d,datensatz);
write('datensatz[',filepos(d),'] ');
zeigen(datensatz);
end;
close(d);
readkey;
end;


Procedure datensatz_uebergeben(Var datensatz:dateityp);
begin
repeat
Readln(datensatz);
until gross(datei,datensatz);
end;

procedure eingeben(var datensatz:dateityp);
begin
clrscr;
writeln('Eingabe(int): ');
repeat
readln(datensatz);
until gross(datei,datensatz);
end;

Function neuiskorr(var d:tdatei;satznr,neu:dateityp):boolean;
var anz,vorgaenger,nachfolger:dateityp;
tempkorr:boolean;
begin
assign(d,dateiname);
reset(d);
anz:=filesize(d);
close(d);
if anz=0 then exit;

if satznr=1 then
begin
assign(d,dateiname);
reset(d);
seek(d,satznr);
read(d,nachfolger);
tempkorr:=neu <= nachfolger;
close(d);
end;

if(satznr=anz) and (anz>1) then
begin
assign(d,dateiname);
reset(d);
seek(d,satznr-2);
read(d,vorgaenger);
tempkorr:=neu>=vorgaenger;
close(d);
end;

if (satznr<anz) or (satznr>1) then
begin
assign(d,dateiname);
reset(d);
seek(d,satznr-1);
read(d,vorgaenger);
seek(d,satznr);
read(d,nachfolger);
tempkorr:=(neu>=vorgaenger) and (neu<=nachfolger);
close(d);
end;

if not (tempkorr) then
begin
neuiskorr:=tempkorr;
end;
end;

Procedure datensatz_aendern(var d:tdatei);
var satznr,ds,anz,neu:dateityp;
begin
assign(d,dateiname);
reset(d);
anz:=filesize(d);

while not(EOF(d)) do
begin
read(d,ds);
zeigen(ds);
end;
close(d);

repeat
writeln('eingabe neu(NR.): ');
readln(satznr);
until satznr IN [1..anz];

repeat
writeln('Datensatz neu: ');
readln(neu);
until neuiskorr(d,satznr,neu);

assign(d,dateiname);
reset(d);
seek(d,satznr-1);
write(d,neu);
close(d);
end;

Procedure menue;
var t:char;
begin
repeat
repeat
clrscr;
writeln('F1 - INIT');
writeln('F2 - Eingeben + Speichern');
writeln('F3 - Laden + Ausgeben');
writeln('F4 - Aendern');
writeln('ESC - QUIT');
t:=readkey;
until t in [#27,#59,#60,#61,#62];
case t of
#59:init(datei);
#60:begin eingeben(datensatz);save(datei,datensatz); end;
#61:load(datei);
#62:datensatz_aendern(datei);
end;
until t=#27;
end;

begin
menue;
end.


Felix Kaiser
28.10.2004, 21:03
Also das grundliegende Problem ist, du hast keine Behandlung von E/A Fehlern drin, weshalb du bei entsprechenden Konflikten fleißig Runtime Errors kassieren wirst. E/A Fehler behandelt man, in dem man vor der Operation die Prüfung deaktiviert {$I-}, anschließend mit der Funktion IOResult einen eventuellen Fehlercode abfragt und wenn man fertig hat, aktiviert man die Prüfung wieder {$I+}.

Dann hast du ganz speziell noch einen Denkfehler drin. Du öffnest die Datei, prüfst die Dateigröße. Du solltest vielleicht prüfen, ob die Dateigröße mindestens einem Datenelement entspricht. Ein Integer hat unter Pascal 16-Bit (2 Bytes). Also sollte die Dateigröße mindestens 2 sein. Besser wäre größer 0 und nicht ungerade (Funktion Odd). Dann suchst du eine Dateiposition Dateiende-1 Byte. Womit du wieder beim Leseproblem wärst. Ein Datensatz hat 2 Bytes. Also musst du entsprechend 2 Bytes vor das Dateiende springen, damit er nicht versucht über das Dateiende hinaus zu lesen.

Und nun machs besser. :)

rswm
30.10.2004, 19:51
ich will nicht klug erscheinen, doch es soll hier nur als Abrundung sein und nichts falsches stehenlassen.

IOResult ist keine funktion, sondern eine systeminterne Variable.

ansonsten kann ich dem Felix nur beipflichten.

RSWM

Felix Kaiser
31.10.2004, 18:36
IOResult ist eine Funktion. Diese gibt nur den Wert einer Variable zurück, die einen E/A Fehlercode enthalten kann und setzt diese auf 0. Wenn eine E/A Funktion aufgerufen wird, die diese Variable setzt und die Variable ist ungleich 0, wird ein Laufzeitfehler ausgelöst, wenn die E/A Prüfung über die Option $I+ aktiviert ist.

wenzi496
02.11.2004, 20:30
@ kaiser

vielen dank schonmal.
kannst du mir vielleicht den genauen ort des problems im programm nennen?

Felix Kaiser
03.11.2004, 16:15
Speziell die Stellen, an denen du die Datei öffnest mit Reset und Rewrite, üblicherweise stellt man vor dem Aufruf {$I-} aus, danach {$I+} wieder an und prüft ob IOResult <> 0. Wenn der Code 0 ist, trat kein Fehler auf. Ansonsten ist es ein DOS E/A Fehlercode. Die andere Stelle ist der Abruf der Dateigröße. Du solltest es mit SizeOf(DateiTyp) vergleichen, statt mit 0. Eleganter ist aber immer einfach nur mit file zu arbeiten und entsprechend mit BlockRead. Du kannst dort einen Parameter angeben, der eine Variable des Typs Word referenziert, in dem die Anzahl der gelesenen Bytes gespeichert wird. Solange er immer SizeOf(DateiTyp) Bytes gelesen hat, hat er ein ganzes Element gelesen. Schau dir am besten mal untypisierte Dateien an.