Entwickler-Ecke
Dateizugriff - Fehler mit TFileStream
maxk - Do 06.02.03 11:54
Titel: Fehler mit TFileStream
Ich schreibe mit folgender Procedure:
Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| var FS:TFileStream; X:string; begin FS:=TFileStream.Create('C:\Test.TXT',fmCreate); FS.Size:=5; FS.Position:=0; X:='12345'; FS.WriteBuffer(X,length(X)); FS.Free; end; |
Ich bekomme keine Fehlermeldung, allerdings besteht meine Datei (C:\Text.TXT) danach nur noch aus 5 Steuerzeichen statt aus dem Text von X (12345).
Was mache ich falsch?
maxk
Udontknow - Do 06.02.03 12:00
Hi!
Strings kannst du aufgrund ihrer dynamischen Länge nicht einfach so in einen Stream speichern. Du müsstest entweder Shortstrings (z.B: String[50]) verwenden oder folgende Funktionen benutzen:
Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19:
| procedure SaveStrToStream(const Stream: TStream; const Value: String); //Schreibt einen dynamischen String in einen Stream var Len: Word; begin Len := Length(Value); Stream.WriteBuffer(Len, SizeOf(Len)); if Len > 0 then Stream.WriteBuffer(Pointer(Value)^, Len); end;
procedure LoadStrFromStream(const Stream: TStream; var Str: String); //Liest einen dynamischen String aus einem Stream var Len: Word; begin Stream.ReadBuffer(Len, SizeOf(Len)); SetLength(Str, Len); if Len > 0 then Stream.ReadBuffer(Pointer(Str)^, Len); end; |
Cu, :)
Udontknow
maximus - Do 06.02.03 12:07
hi, ich glaub dafür gibt es TStringStream 8)
mfg mx
maxk - Do 06.02.03 12:10
Geht nicht!
Aber wenn ich den ersten Aufruf von WriteBuffer weglasse funzt es.
Thx,
maxk
Udontknow - Do 06.02.03 12:30
@Maximus:
Was, wenn du noch andere Sachen im Stream hast als Strings? Z.B. Records oder Integerwerte hinter dem String? Dann scheidet TStringStream aus.
@MaxK
Du musst auch zum Einlesen dann die Prozedur LoadStrFromStream benutzen.
Also:
Quelltext
1: 2: 3:
| SaveStrToStream(FS,X);
LoadStrFromStream(FS,X); |
Cu, :)
Udontknow
DaRkFiRe - Fr 07.02.03 00:17
Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| var FS:TFileStream; X:string; begin FS:=TFileStream.Create('C:\Test.TXT',fmCreate); FS.Size:=5; FS.Position:=0; X:='12345'; FS.WriteBuffer(X,length(X)); FS.Free; end; |
filestreams können (so glaube ich mal, gelesen zu haben) keinen festen größen zugewiesen werden
mit writebuffer kann man nicht auf den string selbst zeigen - das ist nur ein zeiger (wegen der delphi-internen verarbeitung von zeichenfolgen)
ich würd's so machen wollen:
Quelltext
1: 2: 3: 4: 5: 6: 7: 8:
| var FS:TFileStream; X:string; begin FS:=TFileStream.Create('C:\Test.TXT',fmCreate); X:='12345'; FS.Write(X[1],length(X)); FS.Free; end; |
Udontknow - Fr 07.02.03 09:39
@DarkFire: Ähnlich machen das ja auch oben gepostete Funktionen. Sie tun aber noch mehr.
Wie bekommst du beim Lesen heraus, wie lang der String ist, der nun eingelesen werden muss? :wink:
Schau dir noch mal die geposteten Funktionen an.
Cu, :)
Udontknow
maxk - Fr 07.02.03 10:41
Die Länge des Strings ist mir ja bekannt!
Quelltext
1: 2:
| SetLength(Str,20); FS.ReadBuffer(Str[1],20); |
und andersherum:
Quelltext
1:
| FS.WriteBuffer(Pointer(Str)^,length(Str)); |
Das Funktioniert (es sei denn ich habe einen Schreibfehler im Quelltext :oops: ).
Udontknow - Fr 07.02.03 13:03
Na gut, wenn du die Länge kennst, geht es. Stelle dir aber mal folgendes vor: Du musst 1000 solcher Strings speichern, 90% davon sind aber leer (''). Deine Dateigrösse würde immer konstant 20000 betragen, benutzt du aber die Funktionen, die ich gepostet habe, so ist die Datei viel kleiner, da du so nur ein Word (=2Byte) für die Länge und den vorhandenen Text speicherst.
Für leere Strings würdest du nur 2 Byte benötigen, mit deiner Methode dagegen 20 (Faktor 10!). Haben die Strings eine Länge von 20, so benötigst du mit den Routinen nur unwesentlich mehr (22 Byte).
Und wenn irgendein String doch mal länger sein sollte als 20 Zeichen, hast du Pech gehabt...
Cu, :)
Udontknow
DaRkFiRe - Fr 07.02.03 13:48
Naja - dann müsste man höchstens eine Art Load / Save Engine schreiben, die nach folgendem Prinzip verfährt:
Schreiben:
Länge des Strings (Integer @ 4 Byte / 32 Bit)
String an sich
Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17:
| procedure writeStringToStream(Stream:TStream; S:STRING); var I:INTEGER; begin I:=LENGTH(S); Stream.Write(I,SIZEOF(INTEGER)); Stream.Write(S[1],I); end;
function readStringFromStream(Stream:TStream):STRING; var I:INTEGER; var R:STRING; begin Stream.Read(I,SIZEOF(INTEGER)); R:=STRINGOFCHAR(#0,I); Stream.Read(R[1],I); Result:=R; end; |
Keldorn - Fr 07.02.03 13:53
habt ihr euch schonmal Twriter/Treader angeschaut?
da gibt es bereits haufenweise Procs zum lesen und speichern unterschiedlichester Variablentypen
Frank
DaRkFiRe - Fr 07.02.03 14:25
Naja - ich glaube, das waren die abstrakten Komponenten für das Schreiben in Delphi-Anwendungen - also für Komponenten selbst ( Binär-daten speichern, usw. )
maximus - Fr 07.02.03 14:37
Na klar,
da hat keldron völlig recht. Die filer klassen benutz ich auch immer wieder gerne...die dinger sind einfach genial, wenn man dynamische datei formate erstellen will.
@DaRkFiRe: ich glaub sie sind weder abstrakt, noch komponeten :wink: Man kann damit binäre, als auch text-formate speichern. Die DFM-routinen benutzen sie auch, da hast du recht.
Man kann sie prima zB in seiner eigene klasse einbauen, um solche prozeduren wie: saveToStream, loadFromStream, saveToFile, etc. zu machen.
mfg maximus
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2026 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!