Liste mit Objekten Speichern und Laden

Für Fragen von Einsteigern und Programmieranfängern...
Rhyt
Beiträge: 70
Registriert: Mo 28. Nov 2022, 20:22

Liste mit Objekten Speichern und Laden

Beitrag von Rhyt »

Hab hier mal etwas gesucht... Und auch versucht mit Hilfe von ChatGPT auf die Lösung zu kommen.. aber irgendwie sitze ich seit 2 Tagen drann und schaffe es einfach nicht.

Das ist meine procedure zum speichern

Code: Alles auswählen

procedure TForm2.SaveObjectList;
var
  FileStream: TFileStream;
  MyObject: TBlock;
  I: integer;
  LocationInt: integer;
begin
  try
    // Save the contents of the list to a file
    FileStream := TFileStream.Create('mylist.dat', fmCreate);
    try
      for I := 0 to obList.Count - 1 do
      begin
        MyObject := obList.Items[I];

        // Convert the Location property to an integer
        LocationInt := MyObject.Location.X * 100 + MyObject.Location.Y;

        // Write the values of MyObject to the file
        FileStream.Write(MyObject.Id, SizeOf(MyObject.Id));
        FileStream.Write(MyObject.Color, SizeOf(MyObject.Color));
        FileStream.Write(LocationInt, SizeOf(LocationInt));
        FileStream.Write(MyObject.Text[1], Length(MyObject.Text));
      end;
    finally
      FileStream.Free;
    end;
  finally
  end;
end;       
Und dies ist die Procedure zum Laden.

Code: Alles auswählen

procedure TForm2.LoadObjectList(const filename: string; var list: TList);
var
  FileStream: TFileStream;
  MyObject: TBlock;
  Id: integer;
  Color: integer;
  LocationInt: integer;
  Location: TPoint;
 //Text: ansistring;
  Text: string;
  TextSize: integer;
 begin
  // Load the list from the file
  FileStream := TFileStream.Create(filename, fmOpenRead);
  try
    // Read the values of the objects from the file and add them to the list
    while FileStream.Position < FileStream.Size do
    begin
      MyObject := TBlock.Create(Form2.Canvas);

      // Read the values of MyObject from the file
      FileStream.Read(Id, SizeOf(Id));
      FileStream.Read(Color, SizeOf(Color));
      FileStream.Read(LocationInt, SizeOf(LocationInt));

      // Convert the Location property to an integer
      // LocationInt := MyObject.Location.X * 100 + MyObject.Location.Y;
      // Convert it back with this
      Location.X := LocationInt div 100;
      Location.Y := LocationInt mod 100;


      // Read the Text from the file
            FileStream.Read(TextSize, SizeOf(TextSize));
      //        FileStream.Read(Text, SizeOf(Text));
          SetLength(Text, TextSize);

          FileStream.Read(Text[1], TextSize);



      // Set the properties of MyObject
      MyObject.Id := Id;
      MyObject.Color := Color;
      MyObject.Text := Text;
      MyObject.Location := Location;

      // Add MyObject to the list
      list.Add(MyObject);

    end;
  finally
    FileStream.Free;
  end;
end;
                         
Es kann mal vorkommen das er es schafft etwas zu laden... Dann stimmt meist die Farbe und glaub auch die Position, aber der Text ist elends Lang usw. und oft passiert auch nichts und es hängt.

Ich glaube ich bin nicht weit von der Lösung entfernt. Aber bin gerade an einem Punkt wo ich keine Ahnung mehr hab, und auch nicht weiss nach was ich schauen muss. Es kann doch nicht so schwer sein die paar Properties wieder einem Objekt zu übergeben und vorher aus dem Stream auszulesen... Hat mir jemand bitte eine Lösung?

Benutzeravatar
theo
Beiträge: 10467
Registriert: Mo 11. Sep 2006, 19:01

Re: Liste mit Objekten Speichern und Laden

Beitrag von theo »

Ich sehe nicht, wo du die Textlänge hinschreibst, die du mit

Code: Alles auswählen

 FileStream.Read(TextSize, SizeOf(TextSize));
einlesen willst.
Einfacher wäre sowieso:
https://www.freepascal.org/docs-html/rt ... tring.html
https://www.freepascal.org/docs-html/rt ... tring.html

Rhyt
Beiträge: 70
Registriert: Mo 28. Nov 2022, 20:22

Re: Liste mit Objekten Speichern und Laden

Beitrag von Rhyt »

Ich bin super neu in der Hinsicht... mit deiner Antwort kann ich Garnichts anfangen. Das ist das was ich über Normalen Strings weiss.

Normaler String:

Ein normaler String ist ein Unicode-String, was bedeutet, dass er eine große Auswahl an Zeichen aus verschiedenen Schriften und Sprachen verarbeiten kann.
Allerdings sind normale Strings nicht so flexibel wie ANSI-Strings, wenn es darum geht, verschiedene Textcodierungen zu verarbeiten.
Dies kann beim Lesen von Textdaten aus einer Datei zu Problemen führen, da der String möglicherweise die codierten Zeichen nicht richtig interpretieren kann.
Darüber hinaus sind normale Strings nicht so effizient beim Umgang mit binären Daten, wie beim Lesen eines Objekteigenschafts aus einem Filestream.


ANSI-String:

Ein ANSI-String ist ein flexibler String-Typ, der eine größere Auswahl an Zeichen und Textcodierungen verarbeiten kann.
Dies macht ihn beim Lesen von Textdaten aus Dateien besser geeignet, da er die codierten Zeichen ohne Fehler interpretieren kann.
ANSI-Strings sind auch beim Umgang mit binären Daten, wie beim Lesen eines Objekteigenschafts aus einem Filestream, effizienter.
Allerdings sind ANSI-Strings nicht so gut darin Unicode-Zeichen zu verarbeiten wie normale Strings und können möglicherweise nicht für alle Anwendungen geeignet sein.

und wie hilft das mir jetzt ? Ich hab wohl das Problem mit der Text länge... wusste ich nicht. Und kann auch jetzt nichts damit anfangen. Weill hätte ich es wohl nicht... wie geschrieben " irgendwie sitze ich seit 2 Tagen drann und schaffe es einfach nicht"... hätte ich es wohl hinbekommen. ich habe keine ahnung mehr im Hirn gerade... und mein letzter ausweg war gerade hier in dem Forum schön die zwei funktionen zu posten und zu hoffen das mehr kommt als hey lies die doku dort... jup hab ich schon mehrmals. nicht persönlich angegriffen fühlen bitte ;)

Benutzeravatar
theo
Beiträge: 10467
Registriert: Mo 11. Sep 2006, 19:01

Re: Liste mit Objekten Speichern und Laden

Beitrag von theo »

Rhyt hat geschrieben:
Di 6. Dez 2022, 13:19
und wie hilft das mir jetzt ? Ich hab wohl das Problem mit der Text länge... wusste ich nicht. Und kann auch jetzt nichts damit anfangen. Weill hätte ich es wohl nicht... wie geschrieben " irgendwie sitze ich seit 2 Tagen drann und schaffe es einfach nicht"
Ich habe dir zwei Antworten gegeben.

Nochmal die ERSTE ganz langsam: :wink:
Du versuchst, eine Textlänge einzulesen die du aber vorher nicht in die Datei hinschreibst.
Zu deinem

Code: Alles auswählen

FileStream.Read(TextSize, SizeOf(TextSize));
Müsste ein passendes

Code: Alles auswählen

FileStream.Write(..) 
da sein.
Das fehlt aber.

ZWEITE Möglichkeit
SCHREIBEN

Code: Alles auswählen

FileStream.WriteAnsiString(MyObject.Text);
und LESEN

Code: Alles auswählen

MyObject.Text:=FileStream.ReadAnsiString;

ArchChem
Beiträge: 82
Registriert: Mo 11. Jul 2022, 10:41

Re: Liste mit Objekten Speichern und Laden

Beitrag von ArchChem »

Hallo,

anstatt die Objekte im Binärform zu speichern, könntest du auch eine einfache CSV-Datei erstellen, wo die die vier Werte kommagetrennt und die Objekte zeilengetrennt abspeichert. Dazu erstellst du einfach eine TStringList, und fügst dort für jedes Objekt eine entsprechende Zeile ein. Die Stringlist kannst du dann abspeichern und beim Laden gehst du mit einer for-Schleife zeilenweise durch, splittest die jeweilige Zeile nach dem Trennzeichen und legst das Objekt mit den Werten an.

Rhyt
Beiträge: 70
Registriert: Mo 28. Nov 2022, 20:22

Re: Liste mit Objekten Speichern und Laden

Beitrag von Rhyt »

ich stehe gerade voll auf dem Schlauch :) denn ich dachte in der Save Procedure wäre das das passende write :)

FileStream.Write(MyObject.Text[1], Length(MyObject.Text));

Benutzeravatar
theo
Beiträge: 10467
Registriert: Mo 11. Sep 2006, 19:01

Re: Liste mit Objekten Speichern und Laden

Beitrag von theo »

Rhyt hat geschrieben:
Di 6. Dez 2022, 13:38
ich stehe gerade voll auf dem Schlauch :) denn ich dachte in der Save Procedure wäre das das passende write :)

FileStream.Write(MyObject.Text[1], Length(MyObject.Text));
Jetzt hast du wenigstens die Fehlerstelle gefunden, auf die ich die hingewiesen hatte :wink: .
Aber das ist nicht die Lösung, das steht ja schon da.

So etwas fehlt vorher (nicht getestet):

Code: Alles auswählen

FileStream.Write(Length(MyObject.Text), SizeOf(TextSize));
Du musst angeben, wie viel Text kommt.

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1430
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Lazarus Fixes FPC Stable
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: Liste mit Objekten Speichern und Laden

Beitrag von fliegermichl »

Woher soll die Leseroutine Wissen, wie lang der einzulesende Text ist?
Schreib also zunächst die Länge des Textes in den Stream und dann eine entsprechende Anzahl Textzeichen.

Beim einlesen liest du zuerst die Länge des Textes und dann entsprechend viele Textzeichen.

Edit: Theo war schneller.

Rhyt
Beiträge: 70
Registriert: Mo 28. Nov 2022, 20:22

Re: Liste mit Objekten Speichern und Laden

Beitrag von Rhyt »

Oh man sollte niemals in nem Forum schreiben wenn man Hungrig ist wie sau. Jetzt ist besser.
(übrigens jaaa du hattest völlig recht das mit dem AnsiString funktioniert :) ( hab das auch schon probiert gehabt... aber mit ner völlig abstrusen doku die ich gefunden habe und es hat nicht funktioniert damit deswegen bin ich davon weg, die hats komplett falsch erklärt wie es aussieht)

hm ich glaube was ich nicht ganz Kapiere ist folgendes. (Aus der Doku)

Code: Alles auswählen

public function TStream.Write(
  const Buffer;
  Count: LongInt
):LongInt; virtual; overload;

Write braucht ja irgendwie den denn Buffer also Inhalt und das ist ja der Text an erster Stelle und dann die länge

Im MyObject.Text[1] bekomme ich ja den Text(Buffer), und über die Length Funktion die Länge. habs auch schon ohne 1 versucht usw.
FileStream.Write(MyObject.Text[1], Length(MyObject.Text));

wenn jetzt Length beim ersten parameter gesetzt wird, wird ja nur die länger übergeben aber die funktion braucht ja den text anfürsich und als zweiter wird die größe angegeben aber ich hab das laut doku so verstanden das es die anzahl der zeichen benötigt.

ich glaub das ist mein Problem :)

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6198
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Liste mit Objekten Speichern und Laden

Beitrag von af0815 »

Rhyt hat geschrieben:
Di 6. Dez 2022, 14:01
ich glaub das ist mein Problem :)
Ich glaube das größte Problem ist, das die Programme das machen, was du sagst, nicht das was du meinst. Aber keine Sorge das ist am Anfang normal. Nur hier im Forum sieht man meistens nur einen ganz kleinen Teil des größeren Problems. Oft hilft wenn mehr vom Projekt gepostet wird.

Zusätzlich BITTE die Quelle (Doku) deiner Versuche, besonders wenn die abstrus ist, mit angeben (Link posten). Oft ist die Doku gar nicht so abstrus, nur der Anwendungsfall passt nicht, oder wenn es wirklich ein Problem, besonders in der Wiki oder zum Lazarus zugehörigen Teile gibt, so kann man die fixen (lassen) oder klarer schreiben. Nur ein anonymer Hinweis auf eine Doku bedingt, das die keiner findet.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1430
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Lazarus Fixes FPC Stable
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: Liste mit Objekten Speichern und Laden

Beitrag von fliegermichl »

Du hast schon den richtigen Text geschrieben. Das Problem ist, daß die Leseroutine nicht herausbekommen kann, wie lang der geschriebene Text ist.

Code: Alles auswählen

Stream.write(mytext[1], Length(mytext));
Dieser Code schreibt den Text in den Stream. Was fehlt ist die Information für die Leseroutine, wie lang der geschriebene Text ist. Also besser:

Code: Alles auswählen

Stream.WriteWord(Length(mytext));
Stream.Write(mytext[1], Length(mytext));
Beim einlesen musst du nun zuerst einlesen wie lang der einzulesende Text ist, entsprechend Speicher reservieren und dann erst den eigentlichen Text einlesen.

Code: Alles auswählen

procedure xx(Stream : TStream);
var l : word;
begin
 l := Stream.ReadWord;
 SetLength(mytext, l);
 Stream.Read(mytext[1], l);
end;

Rhyt
Beiträge: 70
Registriert: Mo 28. Nov 2022, 20:22

Re: Liste mit Objekten Speichern und Laden

Beitrag von Rhyt »

hmm hmm hmmm

also funktioniert das ganze so wie bei nem Canvas ? ( im groben zumindest)

ich hab nen Canvas von Form1 z.b.
Und dann gebe ich dem erstmal die Brush Farbe an. Und die bleibt solange bestehen bis ich die Farbe ändere vom Pinsel ?

Ist es auch so mit dem Stream? Ich muss erstmal die Größe angeben in dem Fall mit WriteWord damit er Platz reserviert, und dann schreibe ich was kommt mit der Längen Angabe was reinkommt damit er weiss wann der stream aufhören muss ?
Beim nächsten müsste ich dann wieder dem Ding erstmal sagen was kommen würde von der Länge damit er den Platz reserviert? Sofern sich der Platz Anspruch halt ändern würde.

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6198
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Liste mit Objekten Speichern und Laden

Beitrag von af0815 »

Rhyt hat geschrieben:
Mi 7. Dez 2022, 13:01
Ist es auch so mit dem Stream? Ich muss erstmal die Größe angeben in dem Fall mit WriteWord damit er Platz reserviert, und dann schreibe ich was kommt mit der Längen Angabe was reinkommt damit er weiss wann der stream aufhören muss ?
Beim nächsten müsste ich dann wieder dem Ding erstmal sagen was kommen würde von der Länge damit er den Platz reserviert? Sofern sich der Platz Anspruch halt ändern würde.
Es gibt Stream.WriteXXX(...) das sind Funktionne die klar definierte Werte schreiben. Bei WriteWord zum Beispiel weis das system wieviele Bytes es in den Stream in welcher Reihenfolge schreiben muss. Bei Stream. Write(Puffer, Länge) weis das System auch ganz genau wieviel es aus dem Puffer in den Stream schreiben muss. Erst beim Lesen gibt es das aber, denn jeder Schreiboperation hat die Umgekehrte Lesefunktion, ABER woher soll jetzt die Stream.Read(Puffer, Länge) wissen wie lange der Bereich ist, der gelesen werden muss. Jetzt gibt es 2 Alternativen. Alternative 1, du verwendest ein Puffer mit fixer Länge. Der kann zu kurz zu lange sein, Pech gehabt. Alternative 2 ist, du vermerkst im Stream, bevor du den Puffer schreibst, wie groß der Puffer in Bytes ist. Dann kannst du beim Lesen zuerst diesen Wert auslesen, weisst dann wie groß dein Puffer sein muss und kannst anschliessend die Daten in den Puffer einlesen. Alles kein Hexenwerk, sowas wurde schon in der Computersteinzeit über die serielle Schnittstelle gemacht. Nur wissen muss man es einmal, dann wie gesagt kein Hexenwerk.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Rhyt
Beiträge: 70
Registriert: Mo 28. Nov 2022, 20:22

Re: Liste mit Objekten Speichern und Laden

Beitrag von Rhyt »

Danke für die Erklärungen, so langsam machts bei mir glaub klick...

PascalDragon
Beiträge: 825
Registriert: Mi 3. Jun 2020, 07:18
OS, Lazarus, FPC: L 2.0.8, FPC Trunk, OS Win/Linux
CPU-Target: Aarch64 bis Z80 ;)
Wohnort: München

Re: Liste mit Objekten Speichern und Laden

Beitrag von PascalDragon »

Rhyt hat geschrieben:
Di 6. Dez 2022, 13:19
Normaler String:

Ein normaler String ist ein Unicode-String, was bedeutet, dass er eine große Auswahl an Zeichen aus verschiedenen Schriften und Sprachen verarbeiten kann.
Allerdings sind normale Strings nicht so flexibel wie ANSI-Strings, wenn es darum geht, verschiedene Textcodierungen zu verarbeiten.
Dies kann beim Lesen von Textdaten aus einer Datei zu Problemen führen, da der String möglicherweise die codierten Zeichen nicht richtig interpretieren kann.
Darüber hinaus sind normale Strings nicht so effizient beim Umgang mit binären Daten, wie beim Lesen eines Objekteigenschafts aus einem Filestream.


ANSI-String:

Ein ANSI-String ist ein flexibler String-Typ, der eine größere Auswahl an Zeichen und Textcodierungen verarbeiten kann.
Dies macht ihn beim Lesen von Textdaten aus Dateien besser geeignet, da er die codierten Zeichen ohne Fehler interpretieren kann.
ANSI-Strings sind auch beim Umgang mit binären Daten, wie beim Lesen eines Objekteigenschafts aus einem Filestream, effizienter.
Allerdings sind ANSI-Strings nicht so gut darin Unicode-Zeichen zu verarbeiten wie normale Strings und können möglicherweise nicht für alle Anwendungen geeignet sein.
Äh... jain. FPC unterstützt verschiedene String Typen, welche leicht verschiedene Verhaltensweisen haben:

ShortString: ein 1-Byte String, welcher maximal 255 Zeichen lang sein kann und aus Turbo Pascal Zeiten stammt. Wird er als Variable genutzt, so belegt er immer 256 Byte auf dem Stack.
String[Länge]: eine Abart von ShortString mit einer geringeren Maximallänge (stammt ebenfalls aus Turbo Pascal)
AnsiString: ein 1- oder Multi-Byte String, welcher maximal High(SizeInt)-Zeichen haben kann. Dieser Typ ist referenz-basiert, das heißt auf dem Stack belegt er immer die Größe eines Pointer. Außerdem unterstützt er Copy-On-Write, was heißt, dass es mehrere Verweise auf die gleiche „Instanz” des Strings geben kann. Zudem kann der String unterschiedliche Code Pages haben, wozu neben Ansi Code Pages wie ISO-8859-1 auch UTF-8 zählt (deswegen auch Multi-Byte, da ein Zeichen dann mehrere Bytes umspannen kann).
UnicodeString: analog zu AnsiString jedoch als 2-Byte String und mit UTF-16 Kodierung. Ein Zeichen kann dabei 2 oder 4 Byte umspannen.
WideString: ebenfalls ein 2-Byte String wie UnicodeString, allerdings ohne Referenzzählung und Copy-On-Write. Existiert außerdem als separater Typ nur unter Windows. Auf anderen Betriebssystemen ist er gleichbedeutend mit UnicodeString.

Dann gibt es noch String (ohne eckige Klammer). Dieser Typ hängt von den Einstellungen des Compilers ab (pro Unit!): im Standardmodus von FPC ist dieser Typ gleichbedeutend mit ShortString. Wird {$H+} aktiviert ist er gleichbedeutend mit String (Standard in {$Mode Delphi}). Ist außerdem {$Modeswitch UnicodeStrings} aktiviert (oder {$Mode DelphiUnicode}), dann ist er gleichbedeutend mit UnicodeString.
FPC Compiler Entwickler

Antworten