Entwickler-Ecke

Dateizugriff - Einen Memorystream speichern


Masterrandy - Di 08.04.03 16:22
Titel: Einen Memorystream speichern
Wie kann ich einen MemoryStream mit blockwrite speichern?
Ich habe im MemoryStream formatierten Text aus einem RichEdit-Feld und die Formatierung soll natürlich erhalten bleiben.


maximus - Di 08.04.03 16:27

Was spricht gegen MemoryStream.saveToFile('c:\muku.rofl');
?

Bei'n bischen text braucht man doch kein blockwrite. oder doch?


Masterrandy - Di 08.04.03 17:20
Titel: Was dagegen spricht:
Was dagegen spricht ist, dass ich meine anderen Daten bereits in eine Datei schreibe und den Inhalt des Streams auch darin haben möchte.


maximus - Mi 09.04.03 11:28

Wie wärs wenn du ein FileStream hernimmst, den du im schreibmodus öffnest und die position ans ende setzt...dann mitmemoryStream.saveToStream(FileStream);die daten rannhängst?


Masterrandy - Mi 09.04.03 11:57
Titel: Mein Ansatz
Also momentan verwende ich BlockWrite, da ich verschiedene Felder und Datentypen in eine Datei schreibe. Unter anderem will ich da auch mehrere TMemoryStreams rein schreiben.
Mit BlockWrite schreibe ich in

Quelltext
1:
fileDaten: file;                    

Da kann ich mit SaveToStream nicht hin schreiben oder?

Ich habe noch nicht versucht erst alles, bis auf die MemoryTreams, zu schreiben, danach die Datei zu öffnen und einen MemoryStream dran zu hängen.

Meine Lösung bis jetzt, und sie scheint zu funktionieren, ist jedes Zeichen aus dem MemoryStream in ein Char mit ReadBuffer zu lesen und dann Stück für Stück dran zu hängen.
Jetzt bin ich gerade am Testen ob das auch immer funktioniert. Falls nicht werde ich auch deinen Vorschlag nochmal testen.


maximus - Mi 09.04.03 13:36

Zitat:
ist jedes Zeichen aus dem MemoryStream in ein Char mit ReadBuffer zu lesen und dann Stück für Stück dran zu hängen.


:D Viel uneffizienter kann mans auch nicht machen. Wenn du byte für byte schreibst, brauchst du auch kein BlockWrite. Dann mach lieben alles via streams, oder versuch auf den buffer der streams zuzugreifen.


Masterrandy - Mi 09.04.03 13:41
Titel: Beispiel
Kannst du mir da ein Beispiel zeigen wie du das genau meinst?

Zitat:
Viel uneffizienter kann mans auch nicht machen.

Ich weiß, dass es umständlich ist, aber das ist das einzige, dass bis jetzt funktioniert hat.

Zitat:
Wenn du byte für byte schreibst, brauchst du auch kein BlockWrite.

Das blockwrite brauche ich aber, da ich verschiedene Daten in meiner Datei habe und ich auch speichern muss was für ein Block jetzt kommt mit welcher größe (Jedenfalls habe ich das bisher so verstanden).


Udontknow - Mi 09.04.03 14:06

Hallo!

Hier ein einfacher Stream-Gebrauch:


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:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
var i:integer;
var r:real;
var t:String[50];

procedure LoadFromStream(Stream:TStream);
begin
  Stream.ReadBuffer(i,SizeOf(i));
  Stream.ReadBuffer(r,SizeOf(r));
  Stream.ReadBuffer(t,SizeOf(t));
end;

procedure SaveToStream(Stream:TStream);
begin
  Stream.WriteBuffer(i,SizeOf(i));
  Stream.WriteBuffer(r,SizeOf(r));
  Stream.WriteBuffer(t,SizeOf(t));
end;

procedure DateiSchreiben;
var Stream:TFileStream;
begin
  //Daten speichern
  i:=500;
  r:=1,5;
  t:='Hallo, Welt!';
  Stream:=TFileStream.Create('C:\Testdatei.dat',FmCreate);

  try
    SaveToStream(Stream);
  finally
    Stream.Free;
  end;
end;

procedure DateiLesen;
var Stream:TFileStream;
begin
  Stream:=TFileStream.Create('C:\Testdatei.dat',FmOpenRead);
  try
    LoadFromStream(Stream);
    ShowMessage(IntToStr(i));
    ShowMessage(FormatFloat('0.00',r);
    ShowMessage(t);
  finally
    Stream.Free;
  end;
end;


Wenn du in einem Programm die Routine Dateischreiben aufrufst, werden die Daten durch einen FileStream in eine Datei geschrieben. Rufst du dagegen die Prozedur DateiLesen auf, werden die Variablen aus der Datei gelesen.

Cu,
Udontknow

Moderiert von user profile iconUGrohne: Code- durch Delphi-Tags ersetzt.


maximus - Mi 09.04.03 14:06

Du kannst auch in fileStreams 'blöcke' schreiben:byteCount := FileStream.Write(buffer, BlockLength);

Wär hilfreich wenn du dein file-format beschreiben könntest.


Masterrandy - Mi 09.04.03 14:42
Titel: Meine Datenstruktur
Ich habe folgende Datenstruktur:


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:
type 
  TPosition = packed record 
    Datei: Integer; 
    Start: Integer; 
    Definition: Boolean; 
  end

type 
  TAttribute = packed record 
     Name: String
     Typ: Char; 
     Laenge: Integer; 
     Position: array of TPosition; 
  end

type 
  TDaten = packed record 
     Dateiinhalt: TMemoryStream; 
     Dateinamen: String
  end;

Var 
     arrDateien: array of TDaten;
     arrAttribute: array of TAttribute;


Und diese Daten will ich nun alle in eine Datei schreiben.

Bisher bin ich so vorgegangen beim Aufbau der Datei:


Moderiert von user profile iconUGrohne: Code- durch Delphi-Tags ersetzt.


maximus - Mi 09.04.03 15:22

Also mit FileStreams hast du natürlich viel luxiriösere möglichkeiten diverse verschiedene typen zu speichern! Ich persöhnlich würde sowas mit den Streams TWriter und TReader machen...das würde jetzt aber zu weit gehn :wink:

Wie man mit nem FileStream umgeht ist ja oben beschrieben und deine records kannst allafor i := 0 to length(arrAttribute)-1 do fileStream.write(arrAttribute[i],sizeOf(TAttribute));abspeichern!

Musst halt mal ein paar testreihen machen, um dich an den umgang mit fileStreams zu gewöhnen :wink:
Mit fileStream.position kannst zB. direkt verschiedene stellen anspringen, falls du daten in die header schreiben musst, die vorher noch nicht feststanden.

erstma viel spass.


Masterrandy - Mi 09.04.03 15:27
Titel: Und wie funktioniert das mit MemoryStreams?
Und wie funktioniert das mit MemoryStreams?
Dort kann ich dich keine Standardgröße alla SizeOf(TDaten) angeben, da ja nicht vorherbestimmt ist wie groß die Daten im MemoryStream sind.


Udontknow - Mi 09.04.03 15:47

Ja aber wieso das denn??? ( :wink: ) Es ist genau das gleiche wie mit einem Filestream (oder jedem anderen Stream). Wenn du beispielsweise einen Integerwert speicherst, verbrauchst du 4 Byte Platz, sei es jetzt auf einer Festplatte in einer Datei oder im RAM.

Cu,
Udontknow


maximus - Mi 09.04.03 18:26

Schätze du hast Masterrandy falsch verstanden!

@Masterrandy: Du kannst doch mit memoryStream.size die grösse eines streams bestimmen, den du vorher gefüllt hast?

Edit:

Zitat:
...
-Anzahl der Dateien (int)
+Schleife für die Anzahl

-Name(string)
-Dateiinhalt(als MemoryStream vorhanden)

könnte so aussehen:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
fileStream.writeInteger(length(arrDings)); // -Anzahl der Dateien (int)
for i := 0 to length(arrDings)-1 do // +Schleife für die Anzahl 
begin
  fileStream.writeString(ArrDings[i].name); //-Name(string) 
  fileStream.loadFromStream(ArrDings[i].memoryStream); // -Dateiinhalt(als MemoryStream vorhanden)
end;


oder?

PS: ähm...writeString, writeInteger gibt es natürlich nicht :oops: Hab hab mich da wieder von TWriter und TReader anstecken lassen

Moderiert von user profile iconUGrohne: Code- durch Delphi-Tags ersetzt.


Masterrandy - Do 10.04.03 12:01
Titel: Verwirrt bin
Ich habe jetzt nach dem Beispiel von Udontknow mein Programm umgeschrieben. Ich schreibe auch vor jede Variable die ich da rein schreibe die Größe der Daten. Wenn ich danach die Daten wieder einlese, bekomme ich aber, sobald ich Strings oder den MemoryStream einlese nur Datenmüll geliefert.

Gibt es vllt eine Möglichkeit sich den Inhalt der Datei bitweise anzuschauen? Oder hat jemand eine Ahnung was da schief geht?


maximus - Do 10.04.03 12:19

Keine ahnung!

Zum debuggen würd ich mir eine log-datei anlegen, wo du exakt protokolierst was du schreibst und liest. Dann kannst du shecken, ob die reihenfolge und grössen stimmen.

Wichtig ist, dass du die memoryStream, vor dem speichern, .position := 0 setzt. Mit den strings kann ich mir vorstellen, das die speicher-reservierung, oder so, nicht korrekt ist -> vielleicht mal nen anderen string-typ nehmen!?


wulfskin - Do 10.04.03 13:15
Titel: Re: Verwirrt bin
Masterrandy hat folgendes geschrieben:
Wenn ich danach die Daten wieder einlese, bekomme ich aber, sobald ich Strings oder den MemoryStream einlese nur Datenmüll geliefert. ?
Das liegt daran, des das Programm nicht weiss, wie Groß deine Strings sind. Deshalb kommt er beim Laden ins Schleudern.
Deshalb begrente entweder die Strings auf eine Zeichenanzahl (String[100]), dass ist zwar Platzverbrauchender und einschränkender. geht aber dafür einfacher, oder du schreibst zuerst die Länge und dann den String!

Gruß wulfskin!


Udontknow - Do 10.04.03 13:44

Nimm zum Speichern und Lesen von Strings einfach diese Routinen:


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 10.04.03 13:59

@UDontknow: "Optimismus ist nur ein Mangel an Information." -> das hat harald schmidt letztens zitiert...das selbe trifft auch auf Pessimismus zu :wink:


Masterrandy - Do 10.04.03 14:13
Titel: Stringlänge steht davor
Ich schreibe ja vor den String die Länge und auch vor den Stream.

Wenn ich den Dateinamen von der gleichen Position, wo ich ihn auch geschrieben habe, in einen String lesen will, kann ich mir den Inhalt auch nicht bei Debug|Evaluate anschauen. Füge ich diesen String in einen Anderen String ein, dann erhalte ich einen Zugriffsfehler.


Masterrandy - Do 10.04.03 14:42
Titel: Ok, Strings gehen jetzt
Ok, beim Einlesen der Strings habe ich jetzt keine Probleme mehr. Das Hauptproblem war einfach das ich keine Pointer benutzt habe. (Mit Pointer habe ich einfach irgendwie keinen Durchblick :( )

Jetzt habe ich aber noch ein kleines Problem mit dem Stream den ich gespeichert habe. Hab ich den vllt auch falsch abgespeichert?


Quelltext
1:
2:
3:
intGroesse := StreamText.Size;
StreamText.Position := 0;
Stream.WriteBuffer(StreamText, intGroesse);


Mein bisheriger Versuch des Einlesens:

Quelltext
1:
2:
3:
4:
5:
Stream.ReadBuffer(intGroesse, SizeOf(intGroesse));
Dateien[intI].Dateiinhalt := TMemoryStream.Create;
Dateien[intI].Dateiinhalt.SetSize(intGroesse);   
Dateien[intI].Dateiinhalt.Position := 0;
Stream.ReadBuffer(Dateien[intI].Dateiinhalt, intGroesse);


Wenn ich da auch mit Pointer ran gehe wirft er mir eine Fehlermeldung.

Ansonten tritt kein Fehler auf beim Einlesen. Wenn ich aber dann


Quelltext
1:
Dateien[intI].Dateiinhalt.Position := 0;                    


ausführe, bekomme ich eine access violation.


Udontknow - Do 10.04.03 15:11

@ Masterrandy:

Schaue mal in meinem vorherigen Post, da habe ich zwei Routinen gepostet, die dir eigentlich die ganze Arbeit mit den Strings abnehmen.
Wenn du sie dir anschaust, wirst du bemerken, das vor dem eigentlichen Inhalt die Größe des Strings als Word ("Stream.WriteBuffer(Len, SizeOf(Len));") abgelegt wird. Wo tust du das denn?

@Maximus:
Ja, der Harald hat schon super Zitate! :D Ich war am schwanken, ob ich nicht lieber "Die Welt ist nicht schlecht... Sie ist nur zu voll!" nehmen sollte oder dieses als Zitat.

Cu,
Udontknow


Masterrandy - Do 10.04.03 15:20

Die größe speichere ich schon auch mit ab:


Quelltext
1:
Stream.WriteBuffer(Stream.Size, SizeOf(Stream.Size));                    


Aber wie gesagt: Damit hat das Programm anscheinend keine Probleme mehr. Und die Datei wird auch genau um soviel Bytes größer wie der Stream lang ist. Deshalb gehe ich jetzt mal davon aus, dass der Inhalt korrekt darin gespeichert ist.
Wenn ich nun den Pointer auf 0 setzen will, weil ich auf den neu eingelesenen Streaminhalt zugreifen möchte, wird eine Fehlermeldung geschmissen.


Udontknow - Do 10.04.03 15:22

:?: :?: :?:

Du musst die Länge des Strings doch abspeichern, nicht die Größe des Streams :!:


Masterrandy - Do 10.04.03 15:36
Titel: Wenn du den Code nun zusammen schreibst:
Dann erhält man dies:


Quelltext
1:
2:
3:
4:
intGroesse := StreamText.Size; 
Stream.WriteBuffer(intGroesse, SizeOf(intGroesse));  //Feld gibt an wieviel Zeichen der folgende Stream hat
StreamText.Position := 0; 
Stream.WriteBuffer(StreamText, intGroesse); // Speichert den Stream ab


Udontknow - Do 10.04.03 15:41

Tut mir leid, aber du verwirrst mich!

Was ist streamText? Ist das ein MemoryStream? Dann musst du

Quelltext
1:
Stream.WriteBuffer(Stream.Memory, intGroesse)                    

schreiben.

Aber warum so kompliziert? Warum nicht einfach "SaveStrToStream(Stream,MeinString);" ?

Cu,
Udontknow


Masterrandy - Do 10.04.03 16:08
Titel: Ich bin nicht mehr bei den Strings
Ich möchte jetzt einen MemoryStream speichern und wieder einlesen. Das Speichern und Einlesen der Strings funktioniert doch schon lange, was ich auch oben schon erwähnt habe.
StreamText ist ein MemoryStream in den ich Text geschoben habe.

Was genau bewirkt das:


Quelltext
1:
Stream.WriteBuffer(Stream.Memory, intGroesse)                    


Abspeichern kann ich den MemoryStream auch soviel ich sehen kann, nur beim Laden aus der Datei habe ich noch Probleme.


Udontknow - Do 10.04.03 16:17

Pffff... :?

Ich verstehe zwar noch immer noch nicht, warum du die geposteten Routinen nicht verwenden willst, da du in diesem MemoryStream ja doch nur Text hast, aber wenn es denn unbedingt mit zusätzlichem Code sein muss, bitte schön.

Also, Die Eigenschaft Memory ist ein Pointer auf den Speicherbereich den das TMemorystream für seine Daten benutzt. Wenn du wie in deinem vorigen Post einfach nur Stream.ReadBuffer(MemStream,intGroesse);
schreibst, überschreibst du damit die Objektinstanz des Streams, und nicht den Dateninhalt. Dann kracht es natürlich.

Cu,
Udontknow


maximus - Do 10.04.03 17:14

Ihr seid so wirre :roll:

warum denn mit readBuffer? ->


Quelltext
1:
Stream.LoadFromStream(MemStream);                    


Spricht was dagegen...ich mein streams sind ja dafür da mit streams umzugehen.


Masterrandy - Fr 11.04.03 09:06
Titel: Kleines Problem!!!
Mit LoadFromStream wird der Ganze Stream eingelesen. Ich brauche aber nur einen bestimmten Teil davon.


Masterrandy - Fr 11.04.03 09:32
Titel: Was mache ich hier falsch?
Warum kann ich so keinen Test aus einem RichEdit holen, in einen MemoryStream schreiben und von dort in eine Datei speichern, anschließend von der Datei über den MemoryStream wider ins RichEdit laden?
ACHTUNG: Das ist nur eine Testprocedure um zu sehen wo es hakt, also bitte nicht kommen und fragen warum ich das nicht gleich in eine Datei speichere!!!


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:
34:
35:
36:
37:
procedure TFDoku.test21Click(Sender: TObject);
var       fStream: TFileStream;
          mStream: TMemoryStream;
          intGroesse: Integer;
begin
    fStream := TFileStream.Create('c:\test.doku', fmCreate);
    mStream := TMemoryStream.Create;

    try
      RichEdit.Lines.SaveToStream(mStream);
      intGroesse := mStream.Size;;

      mStream.Position := 0;
      fStream.WriteBuffer(mStream, intGroesse);

    finally
      mStream.Free;
      fStream.Free;
      RichEdit.Lines.Clear;
    end;

    fStream := TFileStream.Create('c:\test.doku', fmOpenRead);
    mStream := TMemoryStream.Create;

    try

      mStream.Position := 0;
      fStream.Position := 0;

      fStream.ReadBuffer(mStream, intGroesse);
      RichEdit.Lines.LoadFromStream(mStream);

    finally
      mStream.Free;
      fStream.Free;
    end;
end;


Wenn ich das ausführe, dann bleibt das Rcihedit leer.


Udontknow - Fr 11.04.03 09:34

Klappt es denn jetzt? Alternativ kannst du noch die Methode CopyFrom anwenden.


Udontknow - Fr 11.04.03 09:37

Liest du eigentlich meine Posts?


Quelltext
1:
2:
3:
fStream.WriteBuffer(mStream, intGroesse); 
...
fStream.ReadBuffer(mStream, intGroesse);


Du musst mStream.Memory verwenden (könnte auch mStream.Memory^ sein), so wie du es machst geht es nicht! Oder verwende besser CopyFrom.

Cu,
Udontknow


Udontknow - Fr 11.04.03 09:44

Es muss Memory^ sein.

So, hier mal die korrigierte Version:


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:
34:
35:
36:
37:
38:
procedure TForm1.Button1Click(Sender: TObject);
var       fStream: TFileStream;
          mStream: TMemoryStream;
          intGroesse: Integer;
begin
    fStream := TFileStream.Create('c:\test.doku', fmCreate);
    mStream := TMemoryStream.Create;

    try
      RichEdit.Lines.SaveToStream(mStream);
      intGroesse := mStream.Size;

      mStream.Position := 0;
      fStream.WriteBuffer(mStream.Memory^, intGroesse);

    finally
      mStream.Free;
      fStream.Free;
      RichEdit.Lines.Clear;
    end;

    fStream := TFileStream.Create('c:\test.doku', fmOpenRead);
    mStream := TMemoryStream.Create;

    try

      mStream.Position := 0;
      fStream.Position := 0;
      mstream.Size:=fStream.Size; //memoryStream-Grösse muss vorher festgelegt werden, ansonsten ist Memory NIL

      fStream.ReadBuffer(mStream.Memory^, intGroesse);
      RichEdit.Lines.LoadFromStream(mStream);

    finally
      mStream.Free;
      fStream.Free;
    end;
end;


Cu,
Udontknow


Masterrandy - Fr 11.04.03 09:55
Titel: Ok, es funktioniert
Hatte es zwar mit mStream.Memory mal probiert, aber nicht mit mStream.Memory^ und daran lag es ja anscheinend gelegen.

Jedenfalls danke danke danke für deine Hilfe und Geduld :P


maximus - Fr 11.04.03 10:09
Titel: Re: Kleines Problem!!!
Schön das es funktioniert!

Aber eins wollte ich noch anmerken:

Masterrandy hat folgendes geschrieben:
Mit LoadFromStream wird der Ganze Stream eingelesen. Ich brauche aber nur einen bestimmten Teil davon.


Soweit ich weiss wolltest du den gesamten TextMemoryStream in die datei laden!? oder nicht?


Masterrandy - Fr 11.04.03 10:16
Titel: Nicht ganz
Da war das schon wieder umgekehrt. Ich wollte aus der Datei in den MomoryStream lesen.


wulfskin - Fr 11.04.03 13:53
Titel: Re: Stringlänge steht davor
Sorry, habe die 2te Seite überlesen (bitte löschen)!


Udontknow - Fr 11.04.03 14:06

Ich denke, Masterrandy wollte es allgemein einmal mit Streams machen, damit er dann später auch andere Sachen wie ein Bitmap einfach in einen Stream einspielen und wieder korrekt herauslesen kann.

Cu,
Udontknow