Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi Nutzung MemoryStream und FileStream (https://www.delphipraxis.net/160531-nutzung-memorystream-und-filestream.html)

BasilBavaria 18. Mai 2011 09:25

Nutzung MemoryStream und FileStream
 
Hai!

Ne Frage, ob ich ein grundsätzliches Denkproblem habe: :idea:

Im Prog werden Formulardaten mittels TWriter in einen MemoryStream geschrieben. Alle String-Felder werden ggfs. auf eine fixe Länge
gebracht.
Somit entsteht am Ende eine feste Struktur (Memorystream.Size = 4933).

Diese wird dann in einem Filestream gespeichert. Bei 10 Einträgen stehen also 10 Datenstrukturen im Filestream.

Vor dem Einfügen wird Pos auf (0,soFromEnd) gesetzt.
Beim Update erolgt das positionieren mittels Seek(SNr * Size,soFromBeginning).
Dem Grunde nach erfolgt also die Abbildung einer typisierten Datei.

Aus irgendeinem Grund funktioniert das ne ganze Zeit ganz gut und dann ist der Stream auf einmal zerschossen.
Beim Lesen erfolgt die Meldung "ungültiger Typ" - das Lesen erfolgt analog dem Schreiben bezogen aus Positionierung, dann werden die Teile mittels TReader gelesen.

:?: Ist diese Vorgehensweise grundsätzlich daneben?
:?: Kann sich die Size des Streams trotz fester Feldlängen ändern? (Dies würde das ganze erklären).

Bin für jeden Vorschlag offen.
Danke und viele Grüße
Der Basil

Neutral General 18. Mai 2011 09:27

AW: Nutzung MemoryStream und FileStream
 
Hallo,

Also erstmal würde ich direkt in den Filestream schreiben. Der Umweg über den Memorystream ist eigentlich nicht nötig!

Also wenn du mit einem leeren Stream anfängst und dann nach und nach alle Daten reinschreibst, dann brauchst du eigentlich nicht zu seeken...

Es wäre eventuell nützlich wenn du etwas Code posten könntest ;)

Gruß
Neutral General

himitsu 18. Mai 2011 09:32

AW: Nutzung MemoryStream und FileStream
 
Du kannst auch
Delphi-Quellcode:
.Position := 4933 * x;
setzen,
aber wenn du der Reihe nach Speicherst und ausliest, brauchst du dieses nicht setzen.
Die relative Position zum Dateiende stimmt ja nur, wenn die Dateigröße korrekt ist.


Zitat:

Zitat von BasilBavaria (Beitrag 1101486)
:?: Kann sich die Size des Streams trotz fester Feldlängen ändern? (Dies würde das ganze erklären).

Nein, es sei denn du machst was falscht ... z.B. unterschiedliche Größen des Memory-Streams.

Ich würde an deiner Stelle vor dem Speichern die Größe des MemoryStreams prüfen
Delphi-Quellcode:
is MS.Size <> 4933 then
  Fehlermeldung;
Und vor dem Auslesen, bzw. vor/nach dem Speichern die Dateigröße, sowie die aktuelle Position.
Delphi-Quellcode:
if (FS.Size mod 4933 <> 0) or (FS.Position mod 4933 <> 0) then
  Fehlermeldung;

BasilBavaria 18. Mai 2011 09:39

AW: Nutzung MemoryStream und FileStream
 
Also - der FileStream wird nur einmal angelegt und liegt als Datendatei vor (Lesen und Schreiben). Daher das Positionieren.
Er wird also quasi wie ne Datenbank genutzt.
Je nach nach Zugriff werden 0-n Sätze gelesen via Index und müssen dann bei Änderung upgedatet werden. Auch dafür ist die Positionierung nötig (und der Vergleich mit der typisierten Datei).
Der ganze FileStream kann am Ende mehrere 100 Ds umfassen - die sollen nicht alle im Speicher stehen.

:thumb: Die Prüfroutinen zur Stream.Size sind nicht überall drin. Werd ich mal ergänzen.

Sir Rufo 18. Mai 2011 09:41

AW: Nutzung MemoryStream und FileStream
 
Ohne den Thread jetzt hier sprengen zu wollen, aber wäre es nicht sinnvoller über die Verwendung einer kleine Datenbank nachzudenken?
(z.B. SQLite, Firebird embedded)
Dann hast du mit dem ganzen Stream-Gedöns nix mehr am Hut

p80286 18. Mai 2011 10:36

AW: Nutzung MemoryStream und FileStream
 
Zitat:

Zitat von BasilBavaria (Beitrag 1101486)
Aus irgendeinem Grund funktioniert das ne ganze Zeit ganz gut und dann ist der Stream auf einmal zerschossen.
Beim Lesen erfolgt die Meldung "ungültiger Typ" - das Lesen erfolgt analog dem Schreiben bezogen aus Positionierung, dann werden die Teile mittels TReader gelesen.

Was ist "eine ganze Zeit lang"?

Im Prinzip liest sich das ganz richtig (nur das seek(0,sofromEnd) für das Einfügen? eher Anhängen)
(ich würde übrigens enen Puffer füllen und dann schreiben ohne diese Memory-Teil)

Wo erfolgt die Fehlermeldung?
Wie wäre es mit ein wenig Code?

Gruß
K-H

@Sir Rufo
für 20Kb gleich einen Feuervogel anschleppen?

shmia 18. Mai 2011 10:48

AW: Nutzung MemoryStream und FileStream
 
Zitat:

Zitat von BasilBavaria (Beitrag 1101486)
Somit entsteht am Ende eine feste Struktur (Memorystream.Size = 4933).
Diese wird dann in einem Filestream gespeichert. Bei 10 Einträgen stehen also 10 Datenstrukturen im Filestream

Sehr gefährlich darauf zu vertrauen, dass es immer 4933 Bytes sind.
Irgendwo ein Byte mehr oder weniger (und das ist eigentlich bei TReader/TWriter zwangsläufig so) und die gesamten Daten sind unlesbar.
Was hier fehlt ist eine Art Archivstruktur, die kein Problem damit hat, dass die Einträge unterschiedlich lang sind.

BasilBavaria 18. Mai 2011 14:01

AW: Nutzung MemoryStream und FileStream
 
Hab zur Zeit leider keinen Zugriff auf den Quellcode - aber die Datenstruktur sieht ungefähr aus Kopf so aus:

Code:
tKarte = record
     SNr    : integer;        
     ID     : Cardinal;    
     Del    : boolean;    
     KID    : Cardinal;  
     CDate  : TDateTime;
     L1,
     L2,
     L3,
     L4,
     L5,                   //echte Länge der Daten
     L6      : word;       //Längenfelder für 6 strings
     Titel  : string;     //255
     Info1   : string;     //255
     Info2   : string;     //255
     DokFile : string;     //L=1024
     WWWRef : string;     //L=1024
     Memo   : string;     //L=2048
     Termin : TDateTime;
     TStatus : byte;      
  end;
Im Ergebnis kommen dabei 4933 Byte raus.
L1-L6 haben die echte Feldlänge der nachfolgenden Strings zum Inhalt
Felder kleiner der definierten Länge werden künstlich erweitert auf die definierte Länge, in etwa Info1 := Erw(Edit1.Text,255)

Insofern dürfte sich kein Feld bzw. der ganze Stream von der Größe her ändern.

Das mit der Größenprüfung bau ich mal erweitert ein und meld mich dann wieder.

Danke und noch nen schönen Tach ...

rollstuhlfahrer 18. Mai 2011 14:17

AW: Nutzung MemoryStream und FileStream
 
du könntest deine Struktur noch um 4 Bytes vornedran erweitern (4 Bytes = Integer). Dieser Integer gibt dann sicherheitshalber an, wie lange dein Datensatz ist. Die Länge musst du nicht verwenden, aber kannst dich im Fehlerfall schnell zwischen den Strukturen hin und her bewegen und diese auch in verschiedene Dateien splitten. Somit wären im Fehlerfal auch nicht gleich alle Daten weg.

Zitat:

Zitat von BasilBavaria (Beitrag 1101531)
aber die Datenstruktur sieht ungefähr aus Kopf so aus:

Code:
tKarte = record
     SNr    : integer;        
     ID     : Cardinal;    
     Del    : boolean;    
     KID    : Cardinal;  
     CDate  : TDateTime;
     L1,
     L2,
     L3,
     L4,
     L5,                   //echte Länge der Daten
     L6      : word;       //Längenfelder für 6 strings
     Titel  : string;     //255
     Info1   : string;     //255
     Info2   : string;     //255
     DokFile : string;     //L=1024
     WWWRef : string;     //L=1024
     Memo   : string;     //L=2048
     Termin : TDateTime;
     TStatus : byte;      
  end;

Autsch. Das Problem bei der Sache dürfte wohl sein, dass deine Strings wohl nicht immer die gleiche Länge haben ;)
Ansonsten schöne Struktur. Kann man prima in eine DB speichern.

Zitat:

Zitat von p80286 (Beitrag 1101501)
@Sir Rufo
für 20Kb gleich einen Feuervogel anschleppen?

Warum nicht. Dann hat die DB das Problem mit dem Speichern und die kann das. Außerdem könnten es ja mehr Daten werden.

Bernhard

Neutral General 18. Mai 2011 15:05

AW: Nutzung MemoryStream und FileStream
 
Du kannst/solltest statt "String" einen "ShortString" oder einen String[X] (X = 1 bis 255) benutzen.
Wenn dir für alle Felder maximal 255 Zeichen reichen, dann ist das kein Problem und das Laden & Speichern des Records wird ein Kinderspiel!

Edit: Ich sehe grade du hast auch längere Strings. Dann kannst du alternativ
Delphi-Quellcode:
Array[0..Länge-1] of (Ansi)Char
benutzen! Delphi behandelt die Arrays wie PChars und deswegen kannst du damit fast so arbeiten wie mit richtigen Strings!

rollstuhlfahrer 18. Mai 2011 17:03

AW: Nutzung MemoryStream und FileStream
 
Wo wir eh gerade bei Chars und AnsiChars/WideChars sind: Falls du noch Strings und Chars hast, die auf AnsiStrings und AnsiChars verweisen, ist deine Struktur spätestens nach einem Wechsel auf Delphi >= 2009 im Eimer. Also am Besten gleich WideStrings und WideChars verwenden. Dann sind die auch kompatibel und die Struktur ändert sich erst wieder mit der Einführung von UTF-32.

Bernhard

ADD: Turbo Delphi hat noch AnsiStrings und AnsiChars (sowie PAnsiChars). Wenn du in Erwägung ziehst, irgendwann mal eine neue Delphi-Version einzusetzen, würde ich empfehlen, Wide* zu verwenden, da du dann weniger Probleme hast.

DeddyH 18. Mai 2011 17:09

AW: Nutzung MemoryStream und FileStream
 
Das gilt allerdings nicht für Shortstrings, diese sind AFAIK immer ANSI-kodiert.

rollstuhlfahrer 18. Mai 2011 17:15

AW: Nutzung MemoryStream und FileStream
 
Zitat:

Zitat von DeddyH (Beitrag 1101602)
Das gilt allerdings nicht für Shortstrings, diese sind AFAIK immer ANSI-kodiert.

Ja, aus Gründen der Abwärtskompatibilität.

Bernhard

DeddyH 18. Mai 2011 17:59

AW: Nutzung MemoryStream und FileStream
 
Na also, wenn er also in seinem Record lediglich Shortstrings ablegt, sollten die einen Wechsel zu einer aktuellen Delphi-Version auch schadlos überstehen.

himitsu 18. Mai 2011 18:25

AW: Nutzung MemoryStream und FileStream
 
Die Frage ist auch noch, wie genau er seinen Record speichert/ausließt ... das wurde ja bis jetzt noch nicht genannt.

BasilBavaria 19. Mai 2011 08:20

AW: Nutzung MemoryStream und FileStream
 
Ich hab die Größenprüfungen eingebaut und etwas gefunden:

:!: Die Size des Streams ändert sich anscheinend, wenn Umlaute und Sonderzeichen wie 'ß' im String enthalten sind.
Die Erweiterung des String liefert noch einen korrekten String
Code:
Writer.WriteString( StrExt(KartenSatz.Info1,true,cszInfo1) );
:shock: Beim Prüfen vor Update/Schreiben ist der Stream bei einem Umlaut um 4 Byte vergößert (4937 statt 4933) - bei zwei Umlauten aber nur auf 4938 Byte???
Der String "öß cnchjhj Ä ..kjhk ß" liefert 4940 Byte.
Bei Umlauten in zwei Feldern 4941 Byte usw.

:?: Der Fehler scheint also bei Writer zu liegen - aber warum? In der Onlinehilfe hab ich nix gefunden.

Neutral General 19. Mai 2011 08:24

AW: Nutzung MemoryStream und FileStream
 
Dann schreib halt notfalls selbst in den Stream ;)

himitsu 19. Mai 2011 08:29

AW: Nutzung MemoryStream und FileStream
 
Der Record mit ShortStrings kann übrigens direkt in den Stream kopiert werden.

p80286 19. Mai 2011 16:53

AW: Nutzung MemoryStream und FileStream
 
Zitat:

TWriter wird intern vom Komponenten-Streaming-System verwendet, um Informationen zu einer Komponente (z.B. Komponenteneigenschaften, die als public deklariert sind, oder benutzerdefinierte Eigenschaftsdaten) in einen Stream zu schreiben
Das würde ich so verstehen, das TWriter nicht die erste Wahl ist, ein Record in eine Datei zu schreiben.
Außerdem sind "Strings" doch von Natur aus dynamisch. Nur Shortstrings und Array [0..x] of Char/Byte haben eine definierte Länge, da wundert es mich erst einmal überhaupt nicht, das nicht die angenommene Satzlänge in der Datei landet.
Gerüchteweise habe ich vernommen, daß seit einigen Delphi-Versionen SizeOf und Length auch nicht mehr äquivalent sind.

Gruß
K-H

DeddyH 19. Mai 2011 18:10

AW: Nutzung MemoryStream und FileStream
 
Zitat:

Zitat von p80286 (Beitrag 1101843)
Gerüchteweise habe ich vernommen, daß seit einigen Delphi-Versionen SizeOf und Length auch nicht mehr äquivalent sind.

Wie auch, da Length die Länge in Zeichen angibt. Da aber seit Delphi 2009 ein Zeichen nicht mehr genau ein Byte groß ist, müssen sich diese beiden ja unterscheiden.

Sir Rufo 19. Mai 2011 19:46

AW: Nutzung MemoryStream und FileStream
 
Der TWriter ist perfekt um den ganzen Kladeradatsch in einen Stream zu packen, aber wie so oft im Leben, man muss es richtig machen.

Somit empfiehlt es sich die Quellen dahingehend zu durchforsten, wo mittels TWriter die Komponenten gespeichert werden.

Ist man aber darauf angewiesen immer einen festen Block in den Stream zu schreiben, dann ist TWriter absolut nicht geeignet, denn der versucht möglichst "platzsparend" zu speichern.


Alle Zeitangaben in WEZ +1. Es ist jetzt 21:23 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz