Orientierung im FileStream Schreiben und Lesen

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

Filestreams sind die objektorientierte Alternative zu den zuvor behandelten Dateitypen. Sie gleichen den untypsierten Dateien, da sie ebenfalls keine vordefinierte Datensatz-Struktur besitzen müssen. Nahezu alles, was wir bisher gelernt haben, können wir in leicht modifizierter Form hier anwenden. Um mit einer Instanz von TFileStream zu arbeiten, müssen wir diese erzeugen. Der Konstruktor erwartet 2 Parameter, den Dateinamen und den Zugriffsmodus. Zu den uns bereits bekannten Modi gesellt sich fmCreate, welches sich wie Rewrite verhält. Nach dem Aufruf gibt es eine leere geöffnete Datei. Am Ende muss die Instanz mit Free freigegeben werden.

4.11.5.1. Orientierung im FileStream

FileStream.Size gibt die Größe des Streams in Bytes an. Die aktuelle Position innerhalb des Streams liefert FileStream.Position und wird interpretiert in Bytes, als Abstand zum Anfang des Streams. Mit Seek können wir wieder die Position im Stream festlegen. Im Gegensatz zur File-Variante erfordert FileStream.Seek einen weiteren Parameter, welcher in Relation zu einer festen Position im Stream steht. Entweder zum Anfang, zum Ende oder zur aktuellen Stellung. Es gibt folgende Möglichkeiten: Wert Bedeutung soFromBeginning Postion ist danach übergebener Wert Wert = 0 soFromCurrent Position ist danach FileStream.Position + übergebener Wert soFromEnd Position ist danach FileStream.Size + übergebener Wert Wert = 0 Bezieht man die Position auf das Ende des Streams, muss man also einen Wert =0 angeben, da der übergebene Wert aufaddiert wird.

4.11.5.2. Schreiben und Lesen

Zum Lesen und Schreiben aus einem und in einen Stream stehen uns Read und ReadBuffer bzw. Write und WriteBuffer zur Verfügung. Als erster Parameter wird eine Variable erwartet, welche die im zweiten Parameter übergebenen Anzahl an Bytes übergibt bzw. aufnimmt. Die Parameter der Buffer-Varianten unterscheiden sich nicht zu ihrem Pendant. Intern rufen ReadBufferWriteBuffer sogar ReadWrite auf. Im Gegensatz zu Read und Write gibt es bei den Buffer-Varianten aber eine Exception, falls es ein Problem beim Übertragen der Daten gibt. Bei Read und Write muss der Rückgabewert ausgewertet werden, da er die tatsächliche Anzahl gelesener bzw. geschriebener Bytes enthält. Object Pascal im Detail - Dateien www.delphi-treff.de 121 program file_stream; {APPTYPE CONSOLE} uses SysUtils, Classes; const FN = C:\Dokumente und Einstellungen\user\filestream.dat ; procedure SchreibeDatei; var filestream: TFileStream; datum: TDateTime; begin if FileExistsFN then filestream := TFileStream.CreateFN, fmOpenWrite else filestream := TFileStream.CreateFN, fmCreate; try datum := Now; filestream.Seek0, soFromEnd; filestream.WriteBufferDatum, SizeOfdatum; finally filestream.Free; end; end; Falls die Datei bereits existiert, wird sie im Schreibmodus geöffnet, ansonsten neu erstellt. In Datum wird der aktuelle DateTime-Wert geschrieben und danach wird der Satzzeiger am Ende des Streams positioniert. Wurde die Datei gerade neu erstellt, so ist diese Position auch der Anfang des Streams. Dort wird das Datum angehängt. Bei jedem Programmlauf erscheint somit ein DateTime mehr in der Datei. Am Ende wird der Stream freigegeben, abgesichert durch einem Try-Finally-Block. Object Pascal im Detail - Dateien www.delphi-treff.de 122 procedure LeseDatei; var filestream: TFileStream; datum: TDateTime; begin if FileExistsFN then begin filestream := TFileStream.CreateFN, fmOpenRead; try while filestream.Position filestream.Size do begin filestream.ReadBufferdatum, SizeOfdatum; WriteLnDateTimeToStrdatum; end; finally filestream.Free; end; end else WriteLn Keine Datei vorhanden ; end; begin SchreibeDatei; LeseDatei; ReadLn; end. Auch das Einlesen gestaltet sich nach bekannten Mustern. Es wird so lange gelesen, wie die aktuelle Position innerhalb des Streams kleiner ist, als die Größe der Datei. Wollen wir Datenstrukturen speichern, deren Größe beim Einlesen nicht anhand der Datentypen abgelesen werden können wie z.B. dynamische Arrays und Strings, dann behilft man sich mit einem Trick: Beim Speichern schreibt man zuerst die Größe des Typs in die Datei und erst dann den Wert. Beim Lesen können wir somit durch Auslesen der Größe, die Dimension des Typs festlegen. Object Pascal im Detail - Dateien www.delphi-treff.de 123 type TDatensatz = record Zahl: Longint; Wort: AnsiString; end; var Schreibsaetze, Lesesaetze: array of TDatensatz; procedure SchreibeDatei; var filestream: TFileStream; arraylaenge, stringlaenge: Longint; i: Integer; begin filestream := TFileStream.CreateFN, fmCreate; try Arraylänge ermitteln arraylaenge := LengthSchreibsaetze; Schreiben der Arraylänge filestream.WriteBufferarraylaenge, SizeOfarraylaenge; for i := 0 to arraylaenge - 1 do begin filestream.WriteBufferSchreibsaetze[i].Zahl, SizeOfSchreibsaetze[i].Zahl; Stringlänge ermitteln stringlaenge := LengthSchreibsaetze[i].Wort; Schreiben der Stringlänge filestream.WriteBufferstringlaenge, SizeOfstringlaenge; Schreiben des Strings filestream.WriteBufferSchreibsaetze[i].Wort[1], stringlaenge; end; finally filestream.Free; end; end; Es handelt sich also hier um ein dynamisches Array eines Records, welcher unter anderem einen AnsiString enthält. Beim Einlesen gehen wir nun den umgekehrten Weg: Object Pascal im Detail - Dateien www.delphi-treff.de 124 procedure LeseDatei; var filestream: TFileStream; arraylaenge, stringlaenge: Longint; i: Integer; begin if FileExistsFN then begin filestream := TFileStream.CreateFN, fmOpenRead; try Arraylänge einlesen filestream.ReadBufferarraylaenge, SizeOfarraylaenge; Zuweisen der Array-Länge SetlengthLesesaetze, arraylaenge; for i := 0 to arraylaenge - 1 do begin filestream.ReadBufferLesesaetze[i].Zahl, SizeOfLesesaetze[i].Zahl; Stringlänge einlesen filestream.ReadBufferstringlaenge, SizeOfstringlaenge; Zuweisen der String-Länge SetLengthLesesaetze[i].Wort, stringlaenge; Lesen des Strings filestream.ReadBufferLesesaetze[i].Wort[1], stringlaenge; end; finally filestream.Free; end; end else WriteLn Keine Datei vorhanden ; end; Anhand der vorab gelesenen Werte können wir nicht nur die Länge des dynamischen Arrays und der Strings zuweisen, sondern wissen auch, wie viele Bytes wir im nachfolgenden Schritt einlesen müssen. Initialisierung und Darstellung findet im Hauptprogramm statt: Object Pascal im Detail - Dateien www.delphi-treff.de 125 var i: Integer; begin SetLengthSchreibsaetze, 10; for i := 0 to HighSchreibsaetze do begin Schreibsaetze[i].Zahl := i; Schreibsaetze[i].Wort := IntToStri + . Datensatz ; end; SchreibeDatei; LeseDatei; for i := 0 to HighLesesaetze do begin WriteLnLesesaetze[i].Zahl; WriteLnLesesaetze[i].Wort; WriteLn; end; ReadLn; end.

4.11.6. Die Klasse TStringList