Der Dateizeiger Fehlerquellen Die Dateitypen

Object Pascal im Detail - Dateien www.delphi-treff.de 116 wird, ist die Umwandlung verlustbehaftet oder fehlerhaft. Neu ist die boolsche Funktion Eof, die dann True zurück gibt, wenn das Dateiende erreicht ist. Bei untypisierten Dateien ist die Handhabung etwas komplizierter, da der Inhalt der Datei nicht strukturiert sein muss. Diese Dateien werden mit BlockWrite geschrieben und durch BlockRead gelesen. Zusätzliche Parameter sind hier eine Variable, aus der gelesen bzw. in die geschrieben wird und die Anzahl der Datensätze pro Lese- und Schreibvorgang. Diese Anzahl steht in direkter Verbindung zur optionalen Blockgröße in Reset bzw. Rewrite und bestimmt die Menge an Daten, die pro Lese- oder Schreibzugriff verarbeitet werden. Wir nehmen die gerade erzeugte typisierte Datei und lesen sie in Blöcken zu einem Byte aus: procedure LeseDatei; var untypisierteDatei: file; buffer: Byte; begin AssignFileuntypisierteDatei, FN; ResetuntypisierteDatei, 1; Blockgröße = 1 while not EofuntypisierteDatei do begin BlockReaduntypisierteDatei, buffer, SizeOfByte; SizeOfByte = 1 WriteLnbuffer; end; end;

4.11.4.3. Der Dateizeiger

Die momentane Schreib- bzw. Leseposition innerhalb einer Datei wird durch den Dateizeiger beschrieben und nur dort erfolgt der Zugriff auf die Datei. Nach dem Öffnen bzw. Erstellen einer Datei durch Reset bzw. Rewrite, steht der Dateizeiger am Anfang der Datei. Append platziert den Dateizeiger am Ende der Datei, da Text angefügt werden soll. Wir können in typisierten und untypisierten Dateien die Position dieses Dateizeigers mit Seek verändern, weil dort die Datensatzgröße bekannt ist bzw. bei untypsierten Dateien eine Blockgröße angeben wird. Object Pascal im Detail - Dateien www.delphi-treff.de 117 var TypisierteDatei: file of Longint; IntArray: array [ 1 . .5 ] of Longint = 1 , 2 , 3 , 4 , 5 ; i: Integer; Buffer: Longint; begin AssignFileTypisierteDatei, FN; RewriteTypisierteDatei; for i := LowIntArray to HighIntArray do WriteTypisierteDatei, IntArray[i]; i := HighIntArray - 1 ; while i = do begin SeekTypisierteDatei, i; ReadTypisierteDatei, Buffer; WriteLnBuffer; Deci; end; CloseFileTypisierteDatei; ReadLn; end. Obiges schreibt die Zahlen 1 bis 5 in eine Datei und liest sie rückwärts wieder aus. Die 5 Datensätze, also die Positionen 0 bis 4, werden einzeln durch Seek angesprungen, in dem der Dateizeiger jeweils vor dem Datensatz platziert wird. Zudem erkennen wir hier, dass Rewrite die Datei zum Lesen und Schreiben öffnet. Da wir die Position des Dateizeigers also steuern können, können wir auch einzelne Datensätze überschreiben bzw. der Datei anhängen. Die Position, die dazu angestrebt werden muss, ist die Stelle, an der Eof True zurück gibt. Würden wir die Datei von vorne auslesen wollen, so müssten wir Seek mit der Position 0 aufrufen oder die Datei erneut mit Reset öffnen.

4.11.4.4. Fehlerquellen

Bisher haben wir mögliche Fehler weitestgehend ignoriert. Delphi bietet hier aber einige Möglichkeiten an, das Programm sehr viel robuster zu gestalten. Zunächst können wir die Fehlerbehandlung per Compiler-Schalter {i+} und {i-} steuern. Dieser aktiviert bzw. deaktiviert die Überprüfung der letzten EingabeAusgabe-Routine. Ist der Schalter aktiviert - was die Vorgabe ist - so führt ein EA-Fehler zu einer Exception. Bei deaktiviertem Schalter wird keine Exception ausgelöst, der Fehlerstatus muss mit der Funktion IOResult überprüft werden. Diese liefert im Fehlerfall einen Wert ungleich 0 zurück. Liegt ein Fehler vor, dann werden weitere EA-Operationen blockiert. Eine Abfrage von IOResult setzt diesen Fehlerstatus wieder auf 0. Dies zeigt, dass IOResult in allen Fällen abgefragt und ausgewertet werden muss Object Pascal im Detail - Dateien www.delphi-treff.de 118 const FN = C:\Dokumente und Einstellungen\user\textdatei.txt ; var bytearray: array [1..5] of Byte = 1, 2, 3, 4, 5; procedure SchreibeDatei; var textdatei: TextFile; i: Integer; begin AssignFiletextdatei, FN; {i-} Fehlerbehandlung ausschalten Rewritetextdatei; {i+} Fehlerbehandlung einschalten if IOResult = 0 then Abfragen eines möglichen Fehlers begin for i := Lowbytearray to Highbytearray do WriteLntextdatei, bytearray[i]; CloseFiletextdatei; end else WriteLn Fehler beim Erstellen der Datei ; end; Das Beispiel zeigt, dass wir nicht nur Text, sondern auch Zahlen mit WriteLn schreiben können. Die Fehlerbehandlung wird genau für eine EA-Operation ausgeschaltet. Danach fragen wir mit IOResult sofort den Fehlerstatus ab. Wichtig ist hier noch, dass CloseFile nur aufgerufen werden darf, wenn Rewrite, Reset und Append fehlerfrei durchlaufen. Ansonsten wäre die Datei nicht geöffnet und kann somit auch nicht geschlossen werden, was einen Folgefehler hervorrufen würde. Dieses Konzept der Fehlerbehandlung war bereits in Turbo Pascal bekannt. Delphi bietet uns hier aber eine angenehmere Methode die Datei sicher zu schließen - den bereits bekannten Ressourcenschutzblock: Object Pascal im Detail - Dateien www.delphi-treff.de 119 procedure LeseDatei; var textdatei: TextFile; buffer: string; begin if FileExistsFN then Ist Datei vorhanden begin AssignFiletextdatei, FN; Resettextdatei; try while not Eoftextdatei do begin ReadLntextdatei, buffer; WriteLnbuffer; end; finally CloseFiletextdatei; end; end else WriteLn Keine Datei vorhanden ; end; begin SchreibeDatei; LeseDatei; ReadLn; end. Wir überpfüfen mittels FileExists, ob die Datei überhaupt existiert und lesen sie wieder ein. Mit einfachsten Mitteln begegnen wir hier der wohl häufigsten Fehlerquelle in diesem Bereich. Zudem wissen wir, dass CloseFile nur nach einem fehlerlos durchlaufenen Reset ausgeführt werden darf. Somit erklärt sich die Position des Try-Finally-Blocks von selbst. Auffällig an allen Beispielen ist, dass hier durchgehend fundamentale Typen verwendet wurden und ganz gezielt auf generische Typen verzichtet wurde. Das hat den einfachen Grund, dass die Breite eines generischen Typs nur in der aktuellen Implementierung feststeht. Ein fundamentaler Typ belegt auch in Zukunft so viele Bytes wie heute. Beim Schreiben eines Records müssen wir auf die Ausrichtung innerhalb des Records achten. Wenn wir 1 Byte schreiben wollen und die Ausrichtung 4 Bytes beträgt, dann werden 1 Daten-Byte und 3 Leer-Bytes geschrieben. Das führt dazu, dass solch ein Record mehr Platz beansprucht, als die Summe seiner einzelnen Elemente. Je nachdem, wie man die Daten wieder einliest, sind die Grenzen eines Datensatzes verschoben und man erhält Datenmüll. Diese Problematik können wir aber leicht umgehen, indem wir solch einen Verbund als „packed record deklarieren. Die Ausrichtung erfolgt dann an Byte-Grenzen, der kleinsten adressierbaren Object Pascal im Detail - Dateien www.delphi-treff.de 120 Einheit. Alternativ könnte man hier auch den Compiler-Schalter {A} verwenden. Hier, wie auch bei den fundamentalen Typen, gilt nur bedingt das Abwägen von Schnelligkeit und Sicherheit.

4.11.5. Die Klasse TFileStream