Entwickler-Ecke
Dateizugriff - Verschiedene Typen in einer Datei speichern
catweasel - Do 10.01.13 21:25
Titel: Verschiedene Typen in einer Datei speichern
Hi,
Ich habe folgendes Problem:
Ich möchte verschieden Datentypen in einer Datei speichern.
Bevor jetzt aber Vorshläge zum Thema "Records" kommen: Das nützt mir nichts.
Nehmen wir mal folgende Typen:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23:
| type TData=record Foo : integer; Bar : integer; end;
type TDataMap = array of array of TData;
type TDataFile = file of TData;
type TDataClas=class(TObject) private FData : TDataMap; FMapSize : TPoint; FMapName : string; public procedure LoadFromFile(filename:string); procedure SaveToFile(filename:string); end; |
Ich könnte nun ein TDataFile abspeihern und tricksen indem ich die Grösse des Arrays in einem "Header record" packe und zuerst abspeichere. Die Datei hätte dann einen Record mehr als DataMap Elemente hat, aber das würde funktionieren. Aber wie kann ich nun den Daten und den Namen in einer Datei abspeichern?
In diesem Fall kann ich kein Datenfeld als Header missbrauchen (Foo und Bar sind beides Integer). Nur wegen dem Namen ein Datenfeld hinzuzufügen ist "schlecht" weil ich den Namen ja nur einmal pro Datei habe und nicht für jedes Datenfeld. Ich kann auch keinen Record definieren der Daten und den Namen enthält und dann einen Record abspeichern, da TDataMap keine feste Länge hat und daher tabu ist.
Wie speichert man gemischte Daten :?:
Wäre Dankbar für etwas Aufklärung :roll:
Cheers,
Catweasel
rushifell - Do 10.01.13 21:45
So ganz hab ich Dein Problem nicht verstanden. Hier eine Möglichkeit:
Quelltext
1: 2: 3: 4: 5:
| Datentyp (Konstante Länge, z.B. 1 Byte) Länge Beschreibung (Konstante Länge, z.B. 2 Bytes) Beschreibung (variable Länge) Länge der Daten (Konstante Länge, z.B. 4 Bytes) Daten (variable Länge) |
Je nach Datentyp kannst Du den Aufbau der Folgedaten anders gestalten. Prüfsummen zum Schützen vor Manipulation sind auch denkbar.
Gruß
catweasel - Do 10.01.13 22:31
rushifell hat folgendes geschrieben : |
So ganz hab ich Dein Problem nicht verstanden. Hier eine Möglichkeit:
Quelltext 1: 2: 3: 4: 5:
| Datentyp (Konstante Länge, z.B. 1 Byte) Länge Beschreibung (Konstante Länge, z.B. 2 Bytes) Beschreibung (variable Länge) Länge der Daten (Konstante Länge, z.B. 4 Bytes) Daten (variable Länge) |
Je nach Datentyp kannst Du den Aufbau der Folgedaten anders gestalten. Prüfsummen zum Schützen vor Manipulation sind auch denkbar.
Gruß |
Hi,
Hmm, ich hab deine Antwort nicht verstanden.
Wie würde das denn praktisch aussehen wenn ich einen String einen Integer und ein dynamisches array von records speichern will?
Sähe die Datei dann so aus?
(Je ein Byte pro Zeile)
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8:
| 42 6 D e l p h i |
Was meinst du mit "Beschreibung"?
Das läuft also darauf hinaus eine Datei byteweise zusammen und auseinander zu pfriemeln und Datentyoen wie integer, extended und andere mehrbyte Typen aus einzelnen gelesenen Bytes schneidern?
Oder hab ich da was falsch verstanden?
Cheers,
Catweasel
rushifell - Do 10.01.13 22:46
catweasel hat folgendes geschrieben: |
Sähe die Datei dann so aus? |
Im Grunde ja. Mit Datentyp meinte ich jedoch den Aufbau, der Folgedaten. Sorry, hab mich da etwas ungünstig ausgedrückt.
catweasel hat folgendes geschrieben: |
Wie würde das denn praktisch aussehen wenn ich einen String einen Integer und ein dynamisches array von records speichern will? |
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
| Var w:Word; S:String; i:Integer; LengthDynArray:Cardinal; ... Stream.Read(w,Sizeof(Word); Stream.Read(S,w); ShowMessage(S); Stream.Read(i,4); ShowMessage(IntToStr(i)); Stream.Read(LengthDynArray,4); Stream.Read(DynArray,LengthDynArray); |
Nur vom Prinzip her.
jaenicke - Do 10.01.13 22:48
Wobei ich einfach eine Klasse nehmen würde, dieser die Methoden LoadFromStream und SaveToStream, dazu noch eine Klasse für die gesamte Datei, ebenfalls mit diesen Methoden und dazu LoadFromFile und SaveToFile. Dann ist das alles wunderbar gekapselt. Ein Beispiel müsste ich mal schreiben...
Lemmy - Do 10.01.13 23:01
Hallo,
was spricht eigentlich gegen eine XML Datei?
Grüße
catweasel - Do 10.01.13 23:09
jaenicke hat folgendes geschrieben : |
Wobei ich einfach eine Klasse nehmen würde, dieser die Methoden LoadFromStream und SaveToStream, dazu noch eine Klasse für die gesamte Datei, ebenfalls mit diesen Methoden und dazu LoadFromFile und SaveToFile. Dann ist das alles wunderbar gekapselt. Ein Beispiel müsste ich mal schreiben... |
Hab ich doch vor.
Guck mal die TDataClass Definition am Threadanfang an :)
Cheers,
Catweasel
catweasel - Do 10.01.13 23:17
rushifell hat folgendes geschrieben : |
catweasel hat folgendes geschrieben: | Sähe die Datei dann so aus? |
Im Grunde ja. Mit Datentyp meinte ich jedoch den Aufbau, der Folgedaten. Sorry, hab mich da etwas ungünstig ausgedrückt.
catweasel hat folgendes geschrieben: |
Wie würde das denn praktisch aussehen wenn ich einen String einen Integer und ein dynamisches array von records speichern will? |
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
| Var w:Word; S:String; i:Integer; LengthDynArray:Cardinal; ... Stream.Read(w,Sizeof(Word); Stream.Read(S,w); ShowMessage(S); Stream.Read(i,4); ShowMessage(IntToStr(i)); Stream.Read(LengthDynArray,4); Stream.Read(DynArray,LengthDynArray); |
Nur vom Prinzip her. |
Ah ok. Mit FileStreams hatte ich bisher kaum gearbeitet. Immer nur mit typisierten Dateien (im rudimentärsten Fall vom Typ Byte). Eine Frage habe ich aber noch zu:
Delphi-Quelltext
1: 2:
| Stream.Read(LengthDynArray,4); Stream.Read(DynArray,LengthDynArray); |
Erst wird die Länge als 4 Byte Cardinal eingelesen. Das check ich noch
Aber muss ich da nicht dann noch ein
Setlength(DynArray,LengthDynArray); einfügen bevor ich die Daten lese?
Ich werde mich auf jeden Fall mal mit Streams auseinandersetzen.
Danke für den Tip :)
Cheers,
Catweasel
rushifell - Fr 11.01.13 18:57
Für XML gibts doch bestimmt gute Libraries!?
catweasel hat folgendes geschrieben: |
Aber muss ich da nicht dann noch ein Setlength(DynArray,LengthDynArray); einfügen bevor ich die Daten lese? |
Ich nutze immer einen Buffer mit konstanter Größe, lese vom Stream in den Buffer und übergebe dann die Daten an das Dynamische Array. Sollte wahrscheinlich auch direkt gehen, hatte aber damit schon öfter Probleme.
Bei den Arrays musst Du unbedingt auf den Typ achten:
Delphi-Quelltext
1: 2: 3: 4: 5: 6:
| Var ByteArray:Array[1..2] of Byte; IntegerArray:Array[1..2] of Integer; begin ShowMessage(InttoStr(SizeOf(ByteArray))); ShowMessage(InttoStr(SizeOf(IntegerArray))); end; |
Beim Schreiben und Lesen von Strings solltest Du Dir Gedanken machen, ob Du Unicode unterstützen möchtest (würde ich generell empfehlen).
Ich nutze übrigens gerne die TFastFileStream-Klasse von Flamefire:
http://www.entwickler-ecke.de/viewtopic.php?t=100088
Gruß
jaenicke - Fr 11.01.13 21:25
Mit einer MMF wie sie auch in der Klasse verwendet wird kann man auch direkt als Array auf die Daten zugreifen. Da Windows diese automatisch in den RAM spiegelt, geht das extrem schnell.
Das Prinzip siehst du in dieser Unit von mir sehr gut, bei der das ganze Stream drum herum noch nicht drin ist. Deshalb ist die zum Verständnis vermutlich besser.
http://www.entwickler-ecke.de/viewtopic.php?p=607865
catweasel - Sa 12.01.13 12:36
rushifell hat folgendes geschrieben : |
Ich nutze immer einen Buffer mit konstanter Größe, lese vom Stream in den Buffer und übergebe dann die Daten an das Dynamische Array. Sollte wahrscheinlich auch direkt gehen, hatte aber damit schon öfter Probleme.
Gruß |
Hi,
Das mit den FileStreams hab ich das jetzt geblickt :)
Aber ein Problem hab ich noch:
Ich habe eine Klasse (TBaseClass) geschrieben welche zwei Felder einführt: "Name" und "Description" (beides Strings).
Davon abgeleitet ist TChildClassA, welche zwei zusätzliche Felder "Index" (Int64) und "Color" (TColor) einführt.
Die TBaseClass.LoadFromFile bzw. SaveToFile funktioniert ganz gut wenn ich den FileStream nach dem Lesen/Schreiben in der Prozedur wieder freigebe. (Ansonsten bleibt die Datei ja "in Verwendung").
Jetzt möchte ich TChildClassA auch LoadFromFile/SaveToFile Prozeduren geben. Dazu möchte ich aber die "Lese/Schreib-arbeit" für die Felder von TBaseClass nicht duplizieren.
Ich hatte mir gedacht ich ruf einfach die Parent Prozedur über
inherited auf, speichere die zusätzlichen Felder hintendrann. Ob nun TBaseClass oder TChildClassA den "Sack zu" machen muss wollte ich per:
if self is TChildClassA then FDiskFile.Free; testen.
Ich bekomme aber nur Fehler. Keine AccessViolation, sondern eine "External Exception". :shock: Einzelschritt debugging deutet an das das Freigeben des Streams irgendwie scheitert. Aber nur in TChildClassA....
Ich hab mal das Projekt hier eigefügt.
Wenn man das
FDiskFile.Free in TBaseClass nicht auskommentiert, dann funktionierts damit. TBaseClass Objekte lassen sich dann lesen und speichern.
Wo liegt der Fehler :?:
Cheers,
Catweasel
jfheins - Sa 12.01.13 13:51
In der VCL gibt es (fast) immer eine SaveToFile Methode und eine SaveToStream Methode.
Mache es doch genau so: Die SaveToFile öffnet einen Filestream, ruft die andere Methode auf und schließt diesen wieder.
Die SaveToStream Methode kannst du dann überschreiben und die alte mit inherited aufrufen ;-)
catweasel - Sa 12.01.13 13:57
jfheins hat folgendes geschrieben : |
In der VCL gibt es (fast) immer eine SaveToFile Methode und eine SaveToStream Methode.
Mache es doch genau so: Die SaveToFile öffnet einen Filestream, ruft die andere Methode auf und schließt diesen wieder.
Die SaveToStream Methode kannst du dann überschreiben und die alte mit inherited aufrufen ;-) |
Hmmm....Exakt das habe ich versucht und genau das Schliessen klappt nicht.
Wo liegt der Fehler in meinem Quelltext?
Cheers,
Catweasel
jfheins - Sa 12.01.13 14:24
catweasel hat folgendes geschrieben : |
Hmmm....Exakt das habe ich versucht und genau das Schliessen klappt nicht.
Wo liegt der Fehler in meinem Quelltext? |
Du machst es eben nicht exakt wie ich es vorgeschlagen hatte ;-) (SaveTo
Stream <> SaveTo
File)
Ich finde da nur:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:
| procedure TBaseClass.SaveToFile(filename: string); var Len : integer; begin FDiskFile := TFileStream.Create(filename,fmCreate); Len := Length(FName); FDiskFile.Write(Len,SizeOf(Len)); FDiskFile.Write(PChar(FName)^,Len); Len := Length(FDescription); FDiskFile.Write(Len,SizeOf(Len)); FDiskFile.Write(PChar(FDescription)^,Len); if self is TBaseClass then FDiskFile.Free; end; |
Ich habe das hier gemeint:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
| procedure TBaseClass.SaveToStream(AStream: TStream); var Len : integer; begin Len := Length(FName); AStream.Write(Len,SizeOf(Len)); AStream.Write(PChar(FName)^,Len); Len := Length(FDescription); AStream.Write(Len,SizeOf(Len)); AStream.Write(PChar(FDescription)^,Len); end;
procedure TBaseClass.SaveToFile(filename: string); begin FDiskFile := TFileStream.Create(filename,fmCreate); SaveToStream(FDiskFile); FDiskFile.Free; end; |
Die SaveToStream-Methode kannst du dann in der Kind-Klasse überschrieben und aufrufen.
catweasel - Sa 12.01.13 14:44
jfheins hat folgendes geschrieben : |
catweasel hat folgendes geschrieben : |
Hmmm....Exakt das habe ich versucht und genau das Schliessen klappt nicht.
Wo liegt der Fehler in meinem Quelltext? |
Du machst es eben nicht exakt wie ich es vorgeschlagen hatte ;-) (SaveToStream <> SaveToFile)
Ich finde da nur: Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:
| procedure TBaseClass.SaveToFile(filename: string); var Len : integer; begin FDiskFile := TFileStream.Create(filename,fmCreate); Len := Length(FName); FDiskFile.Write(Len,SizeOf(Len)); FDiskFile.Write(PChar(FName)^,Len); Len := Length(FDescription); FDiskFile.Write(Len,SizeOf(Len)); FDiskFile.Write(PChar(FDescription)^,Len); if self is TBaseClass then FDiskFile.Free; end; |
Ich habe das hier gemeint: Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
| procedure TBaseClass.SaveToStream(AStream: TStream); var Len : integer; begin Len := Length(FName); AStream.Write(Len,SizeOf(Len)); AStream.Write(PChar(FName)^,Len); Len := Length(FDescription); AStream.Write(Len,SizeOf(Len)); AStream.Write(PChar(FDescription)^,Len); end;
procedure TBaseClass.SaveToFile(filename: string); begin FDiskFile := TFileStream.Create(filename,fmCreate); SaveToStream(FDiskFile); FDiskFile.Free; end; |
Die SaveToStream-Methode kannst du dann in der Kind-Klasse überschrieben und aufrufen. |
Ah Ok :)
Ich werd das heute Abend mal testen.
Hab ich das richtig verstanden: Die SaveToFile Prozedur ist nur in der BAsisKlasse implementiert und die jeweilige KindKlasse ruft als erstes
inherited SaveToStream(AStream) auf?
Warum muss AStream hier nicht als
var Parameter übergeben werden?
So etwa:
procedure TBaseClass.SaveToStream(var AStream:TStream);
Und dann könnte FDiskFile in der SaveToFile Prozedur auch eine lokale Variable sein. Dann brauche ich gar kein FileStream als Klassenfeld?
Mit dem Überschreiben von Prozeduren hab ich noch nicht so viel Erfahrung (mach ich sonst kaum).
Danke für die Tips :)
Cheers,
Catweasel
WasWeißDennIch - Sa 12.01.13 14:48
Objektinstanzen muss man i.d.R. nicht als Var-Parameter übergeben, da es sich intern um Pointer handelt. Und in den allermeisten Fällen möchte man ja nicht die Instanz an sich ändern, sondern deren Properties.
catweasel - So 13.01.13 14:07
Hi,
vielen Dank an Alle die mit ihren tollen Tips geholfen haben. Echt Super :)
Schaut euch aber nch mal den Sourcecode an und sagt mir ob es da noch formal etwas zu verbessern gibt.
Irgendein Feld oder eine Methode die öffentlicher ist als nötig?
Cheers,
Catweasel
Keldorn - So 13.01.13 19:07
Hallo
würde das ganze noch um die TWriter und TReader ergänzen
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17:
| procedure TBaseClass.SaveToStream(AStream: TStream); Var Writer:Twriter; begin writer:= Twriter.create(AStream,4096); try begin writer.writeInteger(aktuelleVersionsNummer); writer.writeString(...); writer.writeBoolean(...); writer.writefloat(...); writer.writeDate(...); end; finally writer.free; end; end; |
auslesen mit Treader genau andersrum.
Brauchst halt nicht für jeden Datentyp dir extra was einfallen lassen, das ist schon da.
Außerdem ließt es sich meiner Meinung nach besser, da es alles Einzeiler sind. Es wird wahrscheinlich auch zukunftssicherer sein, grade, wenn Du strings schreibst.
Gruß Frank
catweasel - So 13.01.13 19:36
Keldorn hat folgendes geschrieben : |
Hallo
würde das ganze noch um die TWriter und TReader ergänzen
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17:
| procedure TBaseClass.SaveToStream(AStream: TStream); Var Writer:Twriter; begin writer:= Twriter.create(AStream,4096); try begin writer.writeInteger(aktuelleVersionsNummer); writer.writeString(...); writer.writeBoolean(...); writer.writefloat(...); writer.writeDate(...); end; finally writer.free; end; end; |
auslesen mit Treader genau andersrum.
Brauchst halt nicht für jeden Datentyp dir extra was einfallen lassen, das ist schon da.
Außerdem ließt es sich meiner Meinung nach besser, da es alles Einzeiler sind. Es wird wahrscheinlich auch zukunftssicherer sein, grade, wenn Du strings schreibst.
Gruß Frank |
Wow. TReader/TWriter kannte ich auch noch nicht. Ich hab da mal ne Frage zu. Als ich auf der Embacaero Website mir ein paar Sachen dazu durchgelesen habe:
http://docwiki.embarcadero.com/Libraries/XE2/en/System.Classes.TWriter bin ich etwas verwirrt. Dort steht das man TReader/TWriter nicht direkt erzeugen soll weil das sowieso schon Bestandteil der TSTream Klasse ist.
Hab ich das was falsch verstanden? Ich finde uach kaum "Tutorials" zu TREader/TWriter...
Und was ist die Obergrenze für die Buffergröße?. Hab dazu auch nichts gefunden.
Cheers,
Catweasel
Martok - So 13.01.13 20:23
Und bei den passenden TStream-Funktionen steht dann, dass man die auch nicht nutzen soll. Halte ich für Blödsinn; das steht aber allgemein bei vielem dran, was auch nur entfernt mit Component-Streaming zu tun hat.
Ein Hinweis noch: TReader/Writer verwenden zusätzlich zu den Daten jeweils noch eine 1-Byte-Kennung, die den Typ der Daten angibt. Damit hast du gleich eine Art Konsistenzprüfung, brauchst aber auch mehr Platz: WriteInteger() schreibt eben nicht 4 Byte, sondern 5.
catweasel hat folgendes geschrieben : |
Und was ist die Obergrenze für die Buffergröße?. Hab dazu auch nichts gefunden. |
Bis der RAM voll ist ;-)
Sinnvollerweise sollte man das an 4K-Schritten ausrichten, aber kommt natürlich auch drauf an wie viel du schreiben willst. Wenn deine ganze Datei nachher 10MByte groß wird lohnt sich ein großer Buffer (Windows nimmt gern 64k) eher als wenn die Datei sowieso nur 100 Byte wird.
catweasel - Sa 19.01.13 00:31
Martok hat folgendes geschrieben : |
Und bei den passenden TStream-Funktionen steht dann, dass man die auch nicht nutzen soll. Halte ich für Blödsinn; das steht aber allgemein bei vielem dran, was auch nur entfernt mit Component-Streaming zu tun hat.
Ein Hinweis noch: TReader/Writer verwenden zusätzlich zu den Daten jeweils noch eine 1-Byte-Kennung, die den Typ der Daten angibt. Damit hast du gleich eine Art Konsistenzprüfung, brauchst aber auch mehr Platz: WriteInteger() schreibt eben nicht 4 Byte, sondern 5.
catweasel hat folgendes geschrieben : | Und was ist die Obergrenze für die Buffergröße?. Hab dazu auch nichts gefunden. | Bis der RAM voll ist ;-)
Sinnvollerweise sollte man das an 4K-Schritten ausrichten, aber kommt natürlich auch drauf an wie viel du schreiben willst. Wenn deine ganze Datei nachher 10MByte groß wird lohnt sich ein großer Buffer (Windows nimmt gern 64k) eher als wenn die Datei sowieso nur 100 Byte wird. |
Also nachdem das mit den ersten Gehversuchen mit Streams ja ganz gut geklappt hat habe ich versucht das in mein aktuelles Projekt einfliessen zu lassen.
Und schon gibts Probleme. :? Es soll eine Liste von Objekten gelesen/gespeichert werden.
Ich habe den Quellcode mal beigefügt. Das Problem sieht so aus:
-Nur der erste "Record" wird gelesen, und dort auch nicht alle Felder.
-Alle weiteren Records sind leer (bis auf die Defaultwerte.
Es gibt keine Access Violations oder andere Fehler.
Die Anzahl der Records stimmt, nur wo sind die Daten :?:
Ich hab den Programmablauf mitverfolgt. Da scheint irgendwie nichts zu fehlen. Trotzdem wird nix geladen/gespeichert.
Wäre echt toll wenn mir da jemand noch einmal weiterhelfen könnte. :eyes:
Cheers,
Catweasel
PS: Nicht vom Arbeitstitel stören lassen. Ich bastel an einem Spiel im Stil von Civilization. Bis ich fertig bin wird mir sicher noch ein besserer Titel einfallen :wink:
Keldorn - Sa 19.01.13 10:23
Hallo,
kurz angeschaut, fang erstmal bei den Stringlisten an.
kurzes Demo hingeklatscht:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24:
| Var stream:Tstream; sl:Tstrings; begin stream:=TFileStream.Create('D:\test.dat',fmcreate); sl:=TStringList.Create; sl.Add('eintrag 1'); sl.Add('eintrag 2'); sl.SaveToStream(stream); sl.Clear; sl.Add('eintrag Neu'); sl.SaveToStream(stream); stream.free; sl.Free;
stream:=TFileStream.Create('D:\test.dat',fmOpenRead); sl:=TStringList.Create; sl.LoadFromStream(stream); Memo1.Lines.Assign(sl); Memo1.Lines.Add('========='); sl.LoadFromStream(stream); Memo1.Lines.AddStrings(sl); sl.Free; stream.Free; end; |
==> eintrag neu wird nach dem Laden der 1. stringliste zugeordnet, das 2. loadfromstream führt zu nix.
probier das einfach aus. Das Problem ist sl.loadfromstream gugg dir mal den soruce dazu an. Tstrings.savetostream und loadfromstream ist die Größe egal. Savetostream speichert keine "Größe" der stringlist und loadfromstream liest alles von der aktuellen StreamPosition bis
zum ende. Wenn du danach noch was auslesen willst, steht nix mehr im stream drin ;-).
Nimm nicht sl.savetostream sondern speichere z.B. den sl.text als string in den stream.
Gruß Frank
catweasel - Sa 19.01.13 12:58
Keldorn hat folgendes geschrieben : |
Hallo,
kurz angeschaut, fang erstmal bei den Stringlisten an.
kurzes Demo hingeklatscht:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24:
| Var stream:Tstream; sl:Tstrings; begin stream:=TFileStream.Create('D:\test.dat',fmcreate); sl:=TStringList.Create; sl.Add('eintrag 1'); sl.Add('eintrag 2'); sl.SaveToStream(stream); sl.Clear; sl.Add('eintrag Neu'); sl.SaveToStream(stream); stream.free; sl.Free;
stream:=TFileStream.Create('D:\test.dat',fmOpenRead); sl:=TStringList.Create; sl.LoadFromStream(stream); Memo1.Lines.Assign(sl); Memo1.Lines.Add('========='); sl.LoadFromStream(stream); Memo1.Lines.AddStrings(sl); sl.Free; stream.Free; end; |
==> eintrag neu wird nach dem Laden der 1. stringliste zugeordnet, das 2. loadfromstream führt zu nix.
probier das einfach aus. Das Problem ist sl.loadfromstream gugg dir mal den soruce dazu an. Tstrings.savetostream und loadfromstream ist die Größe egal. Savetostream speichert keine "Größe" der stringlist und loadfromstream liest alles von der aktuellen StreamPosition bis zum ende. Wenn du danach noch was auslesen willst, steht nix mehr im stream drin ;-).
Nimm nicht sl.savetostream sondern speichere z.B. den sl.text als string in den stream.
Gruß Frank |
Danke für den Tip. :) Ich dachte die TStringList.SaveToStream() wäre so bequem das die das alles mit der Grösse und so regelt. Ich habe das jetzt der TNameList Klasse hinzugefügt. Mit der Text Eigenschaft der Stringlist wollte ich jetzt nicht experimentieren da ich da schlechte Erfahrungen mit dem Delimiter gemacht habe.
Nun bekomme ich aber einen anderen Error.. "Bitmap is not valid". :?
Gibt es bei der TBitmap.SaveToStream etwas zu beachten, oder wo liegt der Fehlr jetzt :?:
Cheers,
Catweasel
Keldorn - Sa 19.01.13 13:15
du hast doch die Enterprise, also auch die Sourcen. gugg doch einfach nach ;-).
bitmap.loadfromstream:
Delphi-Quelltext
1:
| ReadStream(Stream, Stream.Size - Stream.Position); |
gleiches Problem.
evtl Abhilfe:
beim Speichern und Laden über einen memorystream gehen.
- memorystream erstellen
- Bitmap in den memstream reinspeichern
- in den richtigen stream die größe des memstreams speichern
- dann den memstream
beim Auslesen dann genau andersrum: nur die entsprechenden Bytes aus stream in den memstream auslesen und die Bitmap dann aus dem Memstream laden
Gruß Frank
catweasel - Sa 19.01.13 13:18
Keldorn hat folgendes geschrieben : |
du hast doch die Enterprise, also auch die Sourcen. gugg doch einfach nach ;-).
bitmap.loadfromstream:
Delphi-Quelltext 1:
| ReadStream(Stream, Stream.Size - Stream.Position); |
gleiches Problem.
evtl Abhilfe:
beim Speichern und Laden über einen memorystream gehen.
- memorystream erstellen
- Bitmap in den memstream reinspeichern
- in den richtigen stream die größe des memstreams speichern
- dann den memstream
beim Auslesen dann genau andersrum: nur die entsprechenden Bytes aus stream in den memstream auslesen und die Bitmap dann aus dem Memstream laden
Gruß Frank |
Werd ich gleich mal testen... Und ich muss mein Profil mal updaten... Ich benutz zur Zeit Delphi 7 Second Edition :)
Cheers,
Catweasel
catweasel - Sa 19.01.13 13:38
Hi,
Hab das jetzt mal versucht. Das sieht so aus:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26:
| procedure TIconItem.LoadFromStream(AStream: TFileStream); var MemStream : TMemoryStream; Size : int64; begin inherited; MemStream := TMemoryStream.Create; Size := AStream.Read(Size,SizeOf(Size)); MemStream.Read(AStream,Size); FIcon.LoadFromStream(Memstream); MemStream.Free; end;
procedure TIconItem.SaveToStream(AStream: TFileStream); var MemStream : TMemoryStream; Size : int64; begin inherited; MemStream := TMemoryStream.Create; FIcon.SaveToStream(MemStream); Size := MemStream.Size; AStream.Write(Size,SizeOf(Size)); AStream.Write(MemStream,Size); MemStream.Free; end; |
Leider funktioniert es nicht :? Es werden nur wirre Daten eingelesen und das Programm "hängt sich auf" weil einige Listengrössen mit 7777864 eingelesen werden, etc.
Muss ich beim kopieren von Stream zu Stream nochwas beachten :?:
Vielen Dank für die Hilfe
Cheers,
Catweasel
jfheins - Sa 19.01.13 14:00
Hi,
ja kein Wunder dass das nicht klappt :twisted:
Der Memorystrem ist ja ein Objekt, d.h. hier:
Delphi-Quelltext
1:
| AStream.Write(MemStream,Size); |
Schreibst du die Adresse des Objekts und noch ein paar Mülldaten in deinen Stream. Um an die tatsächlichen daten zu kommen hilft dir die Hilfe:
http://docwiki.embarcadero.com/Libraries/XE2/en/System.Classes.TCustomMemoryStream.Memory
also muss der Code irgendwie so lauten:
Delphi-Quelltext
1:
| AStream.Write(MemStream.Memory,Size); |
Ich glaube aber du musst den Pointer dann noch dereferenzieren - einfach mal ausprobieren :wink:
catweasel - Sa 19.01.13 14:21
jfheins hat folgendes geschrieben : |
Hi,
ja kein Wunder dass das nicht klappt :twisted:
Der Memorystrem ist ja ein Objekt, d.h. hier:
Delphi-Quelltext 1:
| AStream.Write(MemStream,Size); |
Schreibst du die Adresse des Objekts und noch ein paar Mülldaten in deinen Stream. Um an die tatsächlichen daten zu kommen hilft dir die Hilfe:
http://docwiki.embarcadero.com/Libraries/XE2/en/System.Classes.TCustomMemoryStream.Memory
also muss der Code irgendwie so lauten:
Delphi-Quelltext 1:
| AStream.Write(MemStream.Memory,Size); |
Ich glaube aber du musst den Pointer dann noch dereferenzieren - einfach mal ausprobieren :wink: |
JA. Ich hatte verdrängt das Objekte "nur" Referenzen sind :oops:
Hmmm. Aber auf was muss ich das dereferenzieren. Ich habe mit Pointern kaum Erfahrung.
TMemoryStream.Memory ist vom Typ Pointer. Aber dem FileStream fehlt das. Wie soll ich dann den Punkt zum lesen referenzieren. Vielleicht habe ich auch nur etwas mit den Pointern nicht verstanden.... Momentan komme ich hier echt nicht weiter :?
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30:
| procedure TIconItem.LoadFromStream(AStream: TFileStream); var MemStream : TMemoryStream; p : Pointer; Size : int64; begin inherited; MemStream := TMemoryStream.Create; Size := AStream.Read(Size,SizeOf(Size)); p := AStream.Memory; MemStream.Read(p^,Size); FIcon.LoadFromStream(Memstream.Memory); MemStream.Free; end;
procedure TIconItem.SaveToStream(AStream: TFileStream); var MemStream : TMemoryStream; p : Pointer; Size : int64; begin inherited; MemStream := TMemoryStream.Create; FIcon.SaveToStream(MemStream); Size := MemStream.Size; p := MemStream.Memory; AStream.Write(Size,SizeOf(Size)); AStream.Write(p^,Size); MemStream.Free; end; |
Echst super Eure Hilfe :D
Ich habe gerade gesehen es gibt eine CopyFrom() Prozedur. Lässt sich damit etwas anfangen?
Cheers,
Catweasel
catweasel - Sa 19.01.13 19:11
Also, ich habe mir jetzt das ganze nochmal durch den Kopf gehen lassen und beschlossen die Pixel eizeln in den Stream zu packen. Das klappt auch ganz gut. Da ich mich mit Zeigern nicht so auskenne kann mir vielleicht noch jemand einen Tip geben. :)
Meine neue Bitmapklasse sieht so aus:
(nur die Streaming Methoden)
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33:
| procedure TStreamBitmap.LoadFromStream(AStream: TFileStream); var x,y,w,h : longint; Data : TColor; begin AStream.Read(w,SizeOf(w)); Width := w; AStream.Read(h,SizeOf(h)); Height := h; for y := 0 to h-1 do for x := 0 to w-1 do begin AStream.Read(Data,SizeOf(Data)); Canvas.Pixels[x,y] := Data; end; end;
procedure TStreamBitmap.SaveToStream(AStream: TFileStream); var x,y,w,h : longint; Data : TColor; begin w := Width; h := Height; AStream.Write(w,SizeOf(w)); AStream.Write(h,SizeOf(h)); for y := 0 to h-1 do for x := 0 to w-1 do begin Data := Canvas.Pixels[x,y]; AStream.Write(Data,SizeOf(Data)); end; end; |
Es müsste doch jetzt irgendwie möglich sein das per Scanline zu optimieren. Scanline(0) liefert mir doch einen Zeiger auf den ersten Pixel, oder nicht?
Kann ich dann nicht ab da (With*Heigth*BytesProPixel) Bytes lesen/schreiben?
Wenn ich das versuche klapp nichts :?
Cheers,
Catweasel
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 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!