Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   "Stream Lesefehler" bei Laden via TFileStream (https://www.delphipraxis.net/167218-stream-lesefehler-bei-laden-via-tfilestream.html)

Gashunkification 18. Mär 2012 19:18


"Stream Lesefehler" bei Laden via TFileStream
 
Liste der Anhänge anzeigen (Anzahl: 2)
Hallo :)

Ich habe 2 Objeke, ein Item und eine ItemList.

Das Item ist wie folgt aufgebaut

Delphi-Quellcode:
  TItem = record
    ID: Integer; // wird automatisch gesetzt
    Name: String;
    Info: String;
  end; // TItem
Und die ItemList ist eine ganze Klasse, die ein array of TItem hat und dieses dann mit entsprechenden Methoden verwaltet.

Weiterhin habe ich eine Klasse TListHandler, da ich die ItemList in mehreren Forms brauche. Der Listhandler hat auch die Save und Load Methoden. Bei denen mache ich dann offensichtlich etwas falsch:

(lngth ist die Länge des Dynmaischen arrays, die ich in einer seperaten Datei (ListenName.list~) speichere :D)

Delphi-Quellcode:
// Load a existing List from a specific Filepath
function TListHandler.Load(const FilePath, Pathlngth: string): TItemList;
var F_Stream: TFileStream;
    F_tmp: TFileStream;
    i: Integer;
    lngth: Integer;
    Name, Info: String;
begin
  try
    F_Stream := TFileStream.Create(FilePath, fmOpenRead);
    F_tmp := TFileStream.Create(Pathlngth, fmOpenRead);
    F_tmp.ReadBuffer(lngth, SizeOf(lngth));
    for i := 0 to lngth-1 do
    begin
      Name := List.Item[i].Name;
      Info := List.Item[i].Info;
      F_Stream.ReadBuffer(Name[1], SizeOf(Name));
      F_Stream.ReadBuffer(Info[1], SizeOf(Info));
      List.Length := lngth;
    end;
  finally
    F_Stream.Free;
    F_tmp.Free;
  end; // if
end; // Load

//##############################################################################

// Save the whole List as it is to a pre-specified Filepath
procedure TListHandler.Save(const FilePath, Pathlngth: String);
var FileStream: TFileStream;
    F_tmp: TFileStream;
    i: Integer;
    lngth: Integer;
    Name, Info: String;
begin
  try
    FileStream := TFileStream.Create(FilePath, fmCreate);
    F_tmp := TFileStream.Create(Pathlngth, fmCreate);
    lngth := List.Length;
    F_tmp.WriteBuffer(lngth, SizeOf(lngth));
    for i := 0 to List.Length-1 do
    begin
      Name := List.Item[i].Name;
      Info := List.Item[i].Info;
      FileStream.WriteBuffer(Name[1], SizeOf(Name));
      FileStream.WriteBuffer(Info[1], SizeOF(Info));
    end;
  finally
    FileStream.Free;
    F_tmp.Free;
  end; // finally
end; // Save
Anbei (falls notwendig) noch die beiden Klassen TItemList und TListHandler
PS: Beide Klassen sind noch nicht fertig und entsprechende Methoden werden im letzten Zug ausgelagert. :wink:

daywalker9 18. Mär 2012 19:33

AW: "Stream Lesefehler" bei Laden via TFileStream
 
Wenn Du Strings in einem Stream speichern willst, musst Du dir vorher die Länge mit ablegen. Da die Strings unterschiedlich lang sein können, je nachdem, was eingeben wurde.

Ich habe dafür solche Funktionen:

Delphi-Quellcode:
procedure WriteStringToStream(stream : TStream; str : String);

var
  strLength : integer;

begin
  strLength:=length(str);
  stream.Write(strLength,SizeOf(strLength));
  if strLength>0 then
    stream.Write(str[1],strLength*SizeOf(str[1]));
end;

function ReadStringFromStream(stream : TStream) : String;

var
  strLength : integer;
  hs : String;

begin
  result:='';
  stream.read(strLength,SizeOf(strLength));
  if strLength>0 then
  begin
    hs := '';
    SetLength(hs,strLength);
    stream.read(hs[1],strLength*SizeOf(hs[1]));
    result:=hs;
  end;
end;
Anders wäre es bei ShortString, der String ist dann immer 255 Zeichen lang, diesen könntest Du ohne vorheriges Abspeichern der Länge lesen und schreiben

Keldorn 18. Mär 2012 19:45

AW: "Stream Lesefehler" bei Laden via TFileStream
 
Hallo,

du kannst auch mal einen Blick auf TWriter und TReader werfen, die haben für unterschiedliche Typen entsprechende Routinen an Bord.

Gruß Frank

himitsu 18. Mär 2012 20:02

AW: "Stream Lesefehler" bei Laden via TFileStream
 
Und der Stream-Fehler ist vollkommen verständlich.
Denn da ist alles falsch, was man nur falsch machen kann.

In Load:
- Wann wurde die größe der Liste "List" gesetzt?
- Wann wurde die Länge der Strings "Name" und "Info" gesetz?
- SizeOf(Name) gibt die größe des internen String-Pointers zurück. Beim Debuggen hätte dir auffallen müssen, daß 4 Byte garantiert nicht korrekt sein können.
- uvm.

Gashunkification 18. Mär 2012 21:49

AW: "Stream Lesefehler" bei Laden via TFileStream
 
Okay ... ich hab das glaube ich immernoch nicht so ganz verstanden.
(Ihr müsst das entschuldigen, ich arbeite das erste Mal mit FileStreams und mein Programmierlehrer kann das nur in C#, nicht in Delphi x.x)

Habe die Save/Load mal wie folgt angepasst, aber irgendwie steht in den Variablen Name und Info beim Laden nurnoch müll ...

Delphi-Quellcode:
// Load a existing List from a specific Filepath
procedure TListHandler.Load(const FilePath: String);
var
  F_Stream: TFileStream;
  lngth: Integer;
  strlngth: Integer;
  I: Integer;
  name, Info: String;
begin
  try
    F_Stream := TFileStream.Create(FilePath, fmOpenRead);

    F_Stream.ReadBuffer(lngth, SizeOf(lngth));
    List.Length := lngth;

    for I := 0 to List.Length - 1 do
    begin

      F_Stream.ReadBuffer(strlngth, SizeOf(strlngth));
      SetLength(Name, strlngth);
      F_Stream.ReadBuffer(Name[1], strlngth*SizeOf(Name[1]));
      List.Item[i].Name := Name;

      F_Stream.ReadBuffer(lngth, SizeOf(lngth));
      SetLength(Info, lngth);
      F_Stream.ReadBuffer(Info[1], strlngth*SizeOf(Info[1]));
      List.Item[i].Info := Info;

      F_Stream.ReadBuffer(lngth, SizeOf(lngth));
      List.Item[i].ID := lngth;

    end;// for
  finally
    F_Stream.Free;
  end; // finally
end; // Load

// #############################################################################

// Save the whole List as it is to a pre-specified Filepath
procedure TListHandler.Save(const FilePath: String);
var
  F_Stream: TFileStream;
  lngth: Integer;
  strLngth: Integer;
  I: Integer;
begin
  try
    F_Stream := TFileStream.Create(FilePath, fmCreate);

    lngth := List.Length;
    F_Stream.WriteBuffer(lngth, SizeOf(lngth));
    for I := 0 to List.Length - 1 do
    begin

      strlngth := Length(List.Item[I].Name);
      F_Stream.WriteBuffer(strlngth, SizeOf(strlngth));
      F_Stream.WriteBuffer(List.Item[I].Name[1],
        strlngth*SizeOf(List.Item[I].Name[1]));

      strlngth := Length(List.Item[i].Info);
      F_Stream.WriteBuffer(strlngth, SizeOf(strlngth));
      F_Stream.WriteBuffer(List.Item[I].Info[1],
        strlngth*SizeOf(List.Item[I].Info[1]));

      lngth := List.Item[i].ID;
      F_Stream.WriteBuffer(strlngth, SizeOf(strlngth));
    end; // for

  finally
    F_Stream.Free;
  end;
end; // Save

Aphton 18. Mär 2012 22:01

AW: "Stream Lesefehler" bei Laden via TFileStream
 
Ich sehe auf Anhieb keine Fehler.
Ich gehe aber aus, dass der Fehler evt. in der Load Methode liegt:
Delphi-Quellcode:
// load
  List.Length := lngth;
Ist Length ein Property? Falls nicht -> Fehlerquelle!
Setzt der Setter von Length die Arraygröße - Falls nicht -> Fehlerquelle!

Gashunkification 18. Mär 2012 22:04

AW: "Stream Lesefehler" bei Laden via TFileStream
 
Zitat:

Zitat von Aphton (Beitrag 1157288)
Ich sehe auf Anhieb keine Fehler.
Ich gehe aber aus, dass der Fehler evt. in der Load Methode liegt:
Delphi-Quellcode:
// load
  List.Length := lngth;
Ist Length ein Property? Falls nicht -> Fehlerquelle!
Setzt der Setter von Length die Arraygröße - Falls nicht -> Fehlerquelle!

Beides ist der Fall ... :

Delphi-Quellcode:
property Length: Integer read Get_Length write Set_Length;



und ... :

Delphi-Quellcode:
function TItemList.Get_Length: Integer;
begin
  // Dynamic arrays are zero-based !
  Result := High(ItemList) + 1;
end;

procedure TItemList.Set_Length(const New_Length: Integer);
begin
  SetLength(ItemList, New_Length);
end;

Bummi 18. Mär 2012 23:00

AW: "Stream Lesefehler" bei Laden via TFileStream
 
Wenn Du Delphi 2009 + hast solltest Du spätestens von
Bytes = Length(String)* SizeOf(Char)
ausgehen.

Gashunkification 18. Mär 2012 23:46

AW: "Stream Lesefehler" bei Laden via TFileStream
 
Zitat:

Zitat von Bummi (Beitrag 1157292)
Wenn Du Delphi 2009 + hast solltest Du spätestens von
Bytes = Length(String)* SizeOf(Char)
ausgehen.

genau das mache ich doch ... o__o ... Oder? xD

Delphi-Quellcode:
strlngth := Length(List.Item[i].Info); // <-------- Length(String)
F_Stream.WriteBuffer(strlngth, SizeOf(strlngth));
F_Stream.WriteBuffer(List.Item[I].Info[1],
strlngth*SizeOf(List.Item[I].Info[1])); // <-------- Length(String) * SizeOf(Char)

Bummi 19. Mär 2012 06:31

AW: "Stream Lesefehler" bei Laden via TFileStream
 
beim schreiben ...

Gashunkification 19. Mär 2012 08:33

AW: "Stream Lesefehler" bei Laden via TFileStream
 
Vielen Dank nochmal an alle und @Bummi
Das mache ich doch schon sowohl beim Schreiben als auch beim Lesen. :) Das war nicht das Problem.

Habe den Fehler nun gefunden (habe einmal strlngth und lngth vertauscht). Mit dem Code hier geht es jetzt:

Delphi-Quellcode:
procedure TListHandler.Load(const FilePath: String);
var
  F_Stream: TFileStream;
  lngth: Integer;
  strlngth: Integer;
  I: Integer;
  name, Info: String;
begin
  try
    F_Stream := TFileStream.Create(FilePath, fmOpenRead);

    F_Stream.ReadBuffer(lngth, SizeOf(lngth));
    List.Length := lngth;

    for I := 0 to List.Length - 1 do
    begin

      F_Stream.ReadBuffer(strlngth, SizeOf(strlngth));
      SetLength(Name, strlngth);
      F_Stream.ReadBuffer(Name[1], strlngth*SizeOf(Name[1]));
      Item.Name := Name;

      F_Stream.ReadBuffer(strlngth, SizeOf(lngth));
      SetLength(Info, strlngth);
      F_Stream.ReadBuffer(Info[1], strlngth*SizeOf(Info[1]));
      Item.Info := Info;

      F_Stream.ReadBuffer(lngth, SizeOf(lngth));
      Item.ID := lngth;

      List.Item[i] := Item;
    end;// for
  finally
    F_Stream.Free;
  end; // finally
end; // Load

// #############################################################################

// Save the whole List as it is to a pre-specified Filepath
procedure TListHandler.Save(const FilePath: String);
var
  F_Stream: TFileStream;
  lngth: Integer;
  strLngth: Integer;
  I: Integer;
begin
  try
    F_Stream := TFileStream.Create(FilePath, fmCreate);

    lngth := List.Length;
    F_Stream.WriteBuffer(lngth, SizeOf(lngth));
    for I := 0 to List.Length - 1 do
    begin

      strlngth := Length(List.Item[I].Name);
      F_Stream.WriteBuffer(strlngth, SizeOf(strlngth));
      F_Stream.WriteBuffer(List.Item[I].Name[1],
        strlngth*SizeOf(List.Item[I].Name[1]));

      strlngth := Length(List.Item[i].Info);
      F_Stream.WriteBuffer(strlngth, SizeOf(strlngth));
      F_Stream.WriteBuffer(List.Item[I].Info[1],
        strlngth*SizeOf(List.Item[I].Info[1]));

      lngth := List.Item[i].ID;
      F_Stream.WriteBuffer(lngth, SizeOf(lngth));
    end; // for

  finally
    F_Stream.Free;
  end;
end; // Save

Vielen Dank nochmals an alle :) Ihr habt mir sehr geholfen :thumb:
Hier kann nun also geschlossen werden ^^


Alle Zeitangaben in WEZ +1. Es ist jetzt 20:42 Uhr.

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