![]() |
Stream String lesen und schreiben
Ich finde keine aktuellen Infos und oute mich als doof... :-(
Wie schreibt und liest man aktuell Strings in aus aus Streams? Ich dachte TStream.WriteData(S) und TStream.ReadData(S) sollten vom Compiler (inzwischen) generisch aufgelöst werden? Compiliert wird das auch fehlerfrei, es wird abwer nur der Pointer gespeichert. Mache ich etwas falsch? Habe ich die generische Erweiterung falsch interpretiert? Ist das ein Bug? Wie macht man´s heute richtig? Ich dachte Umwege über Buffergröße usw. sollten nicht mehr nötig sein? |
AW: Stream String lesen und schreiben
Am Einfachsten geht das mit "TStringStream.WriteString".
|
AW: Stream String lesen und schreiben
Oder probiere mal sowas:
Delphi-Quellcode:
MyStream.WriteBuffer(MyString[1], Length(MyString) * SizeOf(Char));
|
AW: Stream String lesen und schreiben
Hallo Stahli,
ich benutze folgeden Code um die Inhalte divereser dynamischer Arrays (= "Vektoren") als AnsiString in Streams zu speichern und aus diesen daraus zu laden:
Delphi-Quellcode:
Type
TDynStringVektor = TArray<String>; PDBiA_StreamType = Int32; CONST PDBiA_StreamType_Integer = 32000; PDBiA_StreamType_Double = PDBiA_StreamType_Integer + 200; PDBiA_StreamType_Extended = PDBiA_StreamType_Integer + 400; PDBiA_StreamType_String = PDBiA_StreamType_Integer + 600; PDBiA_StreamType_MPAF_String = PDBiA_StreamType_Integer + 800; ... Procedure WriteStringVektor_To_Stream(CONST Quelle: TDynStringVektor; Stream: TBytesStream); // Dynamisches String-Array in einen Stream umwandeln { Struktur des Streams, der nur Byte-weise gelesen werden kann: ---------------------- 0. StreamType : Int32 (nach eigener Definition) ---------------------- 1. Länge des Arrays : Int32 ---------------------- 2. Länge von String_1 : Int32 3. String_1 : String variabler Länge ---------------------- 4. Länge von String_2 : Int32 5. String_2 : String variabler Länge ---------------------- 6. Länge von String_3 : Int32 7. String_3 : String variabler Länge ---------------------- ... ... ---------------------- n-1. Länge vom LETZTEN String : Int32 n. LETZTER String : String variabler Länge ---------------------- } VAR Len : Int32; StreamType: PDBiA_StreamType; S : AnsiString; Begin Stream.Position:= 0; // Wichtig!! StreamType:= PDBiA_StreamType_String; Stream.WriteBuffer(StreamType, SizeOf(StreamType)); // Header-Eintrag (= Int32) im Stream: StreamTyp Len:= Length(Quelle); Stream.WriteBuffer(Len, SizeOf(Len)); // Erster Eintrag (= Int32) im Stream: Länge des Arrays: IF Len < 0 Then Begin // Fehlerhafter Eintrag im Stream Fehlermeldung('Stream-Fehler: Negative Array-Länge!'); Exit; End; IF Len = 0 Then // Leeres Array: Exit; // Fertig --> Es wird nur die Länge (= 0) in den Stream geschrieben, mehr nicht! For S in Quelle Do Begin // Für jeden String im String-Array wiederholen Len:= Length(S); // Strings werden der Reihe nach eingelesen Stream.WriteBuffer(Len, SizeOf(Len)); // Länge des aktuellen Strings (dynamisch: d.h. jedes Element könnte unterschiedlich lang sein) IF Len <> 0 Then Begin Stream.WriteBuffer(S[1], Len*SizeOf(S[1])); // String speichern. Zählung bei Strings: 1-basiert! Daher S[1] --> Erstes Zeichen im dynamischen String End; End; End;{WriteStringVektor_To_Stream} {-------------------------------} Function LoadStingVektor_From_Stream(Stream: TBytesStream): TDynStringVektor; // Dynamisches String-Array aus einem Stream laden { Struktur des Streams, der nur Byte-weise gelesen werden kann: ---------------------- 0. StreamType : Int32 (nach eigener Definition) ---------------------- 1. Länge des Arrays : Int32 ---------------------- 2. Länge von String_1 : Int32 3. String_1 : String variabler Länge ---------------------- 4. Länge von String_2 : Int32 5. String_2 : String variabler Länge ---------------------- 6. Länge von String_3 : Int32 7. String_3 : String variabler Länge ---------------------- ... ... ---------------------- n-1. Länge vom LETZTEN String : Int32 n. LETZTER String : String variabler Länge ---------------------- } VAR i, Len : Int32; StreamType: PDBiA_StreamType; Begin Stream.Position:= 0; // Wichtig!! Stream.ReadBuffer(StreamType, SizeOf(StreamType)); // Header-Eintrag (= Int32) im Stream: StreamTyp Len:= SizeOf(Len); Stream.ReadBuffer(Len, Len); // Erster Eintrag (= Int32) im Stream: Länge des Arrays: IF Len < 0 Then Begin // Fehlerhafter Eintrag im Stream Fehlermeldung('Stream-Fehler: Negative Array-Länge!'); Exit; End; IF Len = 0 Then Begin // Leeres Array: Exit; // Fertig --> Es stand nur die Länge (= 0) in den Stream geschrieben, mehr nicht! End; SetLength(Result, Len); For i:= 0 To Len-1 Do Begin// 0: dynamisches Array ist NULL-basiert! Stream.ReadBuffer(Len, SizeOf(Len)); // Länge des aktuellen Strings (dynamisch: d.h. jedes Element könnte unterschiedlich lang sein) IF Len < 0 Then Begin // Fehlerhafter Eintrag im Stream Fehlermeldung('Stream-Fehler: Negative String-Länge!'); Exit; End; IF Len > 0 Then Begin SetLength(Result[i], Len); Stream.ReadBuffer(Result[i][1], Len*SizeOf(Char)); // 1: ERSTES Zeichen des Strings End; End; End;{LoadStingVektor_From_Stream} {-------------------------------} |
AW: Stream String lesen und schreiben
Zitat:
|
AW: Stream String lesen und schreiben
Zitat:
![]() .ReadData, .WriteData, .ReadBufferData usw. .WriteBufferData. ![]() ![]() ... |
AW: Stream String lesen und schreiben
Vielen Dank allen.
Ich war der Meinung, Emba hätte das mal einheitlich umgesetzt und eine generische Compilermagie eingebaut. Na ja... Ich habe mir jetzt eine kleine Unit gebaut, die den MemoryStream um zwei Funktionen erweitert. Dazu einfach in allen Units, die die Streamfunktionalität brauchen, die Unit nach der SystemClasses einbinden: (Ich vergesse immer wieder, die diese Klassen-Ersetzung heißt. :oops:) Lieber Wäre mir zwar, WriteData<String>() und ReadData<String>() zu erweitern (um einfach immer diese Methoden nutzen zu können) aber habe keine Ahnung, ob das umsetzbar und dann auch stabil wäre... Mit dem Workaround hier kann ich aber schon ganz gut leben.
Delphi-Quellcode:
unit MyUnit;
interface uses System.Classes, MyMemoryStream, // <---------------- Generics.Collections, System.SyncObjs, Winapi.MMSystem; type
Delphi-Quellcode:
unit MyMemoryStream;
interface uses System.Classes; type TMemoryStream = class(System.Classes.TMemoryStream) public procedure ReadString(var aString: String); procedure WriteString(const aString: String); end; implementation {: ************************************* TMemoryStream ************************************ :} {: ---------------------------------------- public ---------------------------------------- :} procedure TMemoryStream.ReadString(var aString: String); var Reader: TBinaryReader; begin Reader := TBinaryReader.Create(Self); try aString := Reader.ReadString; finally Reader.Free; end; end; procedure TMemoryStream.WriteString(const aString: String); var Writer: TBinaryWriter; begin Writer := TBinaryWriter.Create(Self); try Writer.Write(aString); finally Writer.Free; end; end; end. |
AW: Stream String lesen und schreiben
Zitat:
|
AW: Stream String lesen und schreiben
Aber der Compiler weiß ja, was da für Typen rein gehen.
Insofern könnte er dann im Fall eines "String" auf meine Funktion umleiten und die Länge und Inhalt schreiben bzw. lesen. |
AW: Stream String lesen und schreiben
Woher soll er wissen, dass die Länge gespeichert werden soll?
Man könnte ja auch als PChar 0-terminiert speichern. Aber ja, man könnte natürlich ein ReadData/WriteData, bzw. ReadBufferData/WriteBufferData mit einem String-Typen bauen, aber wie groß soll dann die Größe gespeichert werden? Byte/Word/LongWord ![]() |
AW: Stream String lesen und schreiben
Ich baue ja keine Compiler würde mir aber sowas vorstellen wie:
Delphi-Quellcode:
Eigentlich müsste der Compiler ja wissen, was er gerade verlinkt.
procedure TStream.WriteData(Param);
begin if Param is String then WriteString(String(Param)) else begin was sonst gemacht wird end end; Kann natürlich auch sein, dass die Sichtweise zu naiv ist. |
AW: Stream String lesen und schreiben
Sagen wir mal, sie ist halt stark auf deinen Anwendungsfall fixiert.
|
AW: Stream String lesen und schreiben
Der Compiler weiß es, aber in der Funktion gibt es keinerlei Typinfos, wenn man nur den Zeiger rein gibt.
Delphi-Quellcode:
Beim Letzten werden eigentlich zwei Parameter übergeben,
procedure Write(Param: Pointer);
procedure Write(const Param); procedure Write(Param: Variant); procedure Write(Param: TVarRec); procedure Write(Param: array of const); aber man kann es sich auch einfach als
Delphi-Quellcode:
vorstellen.
procedure Write(Param: array of TVarRec);
Leider wird bei zukünftigen Implementationen immer weniger Compiler-Magic benitzt. Also nicht sowas wie beim alten Read/ReadLn/Write/WriteLn, wo der Aufruf in eine/mehrere kleine Eintzelaufrufe übersetzt wird, für jeden Typen entsprechend. Variante Parameter kann Delphi, aber leider nur aufrufen, denn offiziell kann man sie in Pascal nicht deklarieren. Wie bei der Format-Funktion des C++, ala sprintf. Was man machen kann, ist ein Overload, wie beim ![]() |
Alle Zeitangaben in WEZ +1. Es ist jetzt 00:22 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 by Thomas Breitkreuz