Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Update/Delete Record items in a Stream (https://www.delphipraxis.net/196162-update-delete-record-items-stream.html)

mandoza 27. Apr 2018 10:48

Delphi-Version: 10.2 Tokyo

Update/Delete Record items in a Stream
 
Hi every one , i save my records into a stream i can read them back , but what i want is to be able to update or delete one of my records item then save back .
please how to do that . thank you


Delphi-Quellcode:
Tmyrecord = Record
  Firstname : string ;
  LastName:string;
  ID : integer ;
end;

....

procedure WriteIntegerToStream(Stream: TStream; Value: Integer);
begin
  Stream.WriteBuffer(Value, Sizeof(Value));
end;

procedure WriteStringToStream(Stream: TStream; const Value: String);
var
  S: UTF8String;
  Len: Integer;
begin
  S := UTF8String(Value);
  Len := Length(S);
  Stream.WriteBuffer(Len, Sizeof(Len));
  Stream.WriteBuffer(PAnsiChar(S)^, Len);
end;

function ReadIntegerFromStream(Stream: TStream): Integer;
begin
  Stream.ReadBuffer(Result, Sizeof(Result));
end;

function ReadStringFromStream(Stream: TStream): String;
var
  S: UTF8String;
  Len: Integer;
begin
  Stream.ReadBuffer(Len, Sizeof(Len));
  SetLength(S, Len);
  Stream.ReadBuffer(PAnsiChar(S)^, Len);
  Result := String(S);
end;

Procedure SaveToStream();
var
  SavingStream: TFileStream;
  i, j: Integer;
  myRec:Tmyrecord;
begin
  SavingStream := TFileStream.Create('SAVE.test', fmCreate or fmOpenWrite or fmShareDenyWrite);
  try
      WriteStringToStream(SavingStream, myRec.Firstname);
      WriteStringToStream(SavingStream, myRec.LastName);
      WriteIntegerToStream(SavingStream, myRec.ID);
  finally
    SavingStream.Free;
  end;
end;

procedure LoadFromStream()
var
  LoadingStream: TFileStream;
  i, j: Integer;
  myRec:Tmyrecord;
begin
  LoadingStream := TFileStream.Create('SAVE.test', fmOpenRead or fmShareDenyWrite);
  try
   
      myRec.Firstname:= ReadStringFromStream(LoadingStream);
      myRec.LastName := ReadStringFromStream(LoadingStream);
      myRec.ID := ReadIntegerFromStream(LoadingStream);
     
  finally
    LoadingStream.Free;
  end;
end;

procedure UpdateMyRecLastName(NewLastName:string);
var
  LoadingStream: TFileStream;
  i, j: Integer;
  myRec:Tmyrecord;
begin
  SavingStream := TFileStream.Create('SAVE.test', fmCreate or fmOpenWrite or fmShareDenyWrite);
  try
     
       myRec.LastName:=NewLastName;
      
      {
      Here how to update myRec.LastName value in the LoadingStream
      }
    
  finally
    SavingStream.Free;
  end;
end;

procedure DeleteMyRecLastName();
var
  LoadingStream: TFileStream;
  i, j: Integer;
  myRec:Tmyrecord;
begin
  SavingStream := TFileStream.Create('SAVE.test', fmCreate or fmOpenWrite or fmShareDenyWrite);
  try
      {
      Here how to Delete myRec.LastName value in the LoadingStream
      }
    
  finally
    SavingStream.Free;
  end;
end;

DeddyH 27. Apr 2018 11:01

AW: Update/Delete Record items in a Stream
 
Why not define a specialized list?
Delphi-Quellcode:
uses ..., System.Generics.Collections;

type
  TMyRecList = class(TList<Tmyrecord>)
  public
    procedure LoadFromFile(const Filename: string);
    procedure SaveToFile(const Filename: string);
  end;

Der schöne Günther 27. Apr 2018 11:08

AW: Update/Delete Record items in a Stream
 
And then your next steps will probably be writing
Delphi-Quellcode:
UpdateFirstName
or
Delphi-Quellcode:
UpdateId
methods.

Why not
  1. Load your data from stream
  2. Alter them in memory
  3. Save everything again

It looks like you already have everything you need.

mandoza 27. Apr 2018 11:09

AW: Update/Delete Record items in a Stream
 
Zitat:

Zitat von DeddyH (Beitrag 1400737)
Why not define a specialized list?
Delphi-Quellcode:
uses ..., System.Generics.Collections;

type
  TMyRecList = class(TList<Tmyrecord>)
  public
    procedure LoadFromFile(const Filename: string);
    procedure SaveToFile(const Filename: string);
  end;

Thank you , but what i wanted is to be able to update / delete items from my record then save / read again .
please read the

Delphi-Quellcode:
procedure UpdateMyRecLastName(NewLastName:string);
var
  LoadingStream: TFileStream;
  i, j: Integer;
  myRec:Tmyrecord;
begin
  SavingStream := TFileStream.Create('SAVE.test', fmCreate or fmOpenWrite or fmShareDenyWrite);
  try
     
       myRec.LastName:=NewLastName;
     
      {
     Here how to update myRec.LastName value in the LoadingStream
      }
   
  finally
    SavingStream.Free;
  end;
end;

procedure DeleteMyRecLastName();
var
  LoadingStream: TFileStream;
  i, j: Integer;
  myRec:Tmyrecord;
begin
  SavingStream := TFileStream.Create('SAVE.test', fmCreate or fmOpenWrite or fmShareDenyWrite);
  try
      {
     Here how to Delete myRec.LastName value in the LoadingStream
      }
   
  finally
    SavingStream.Free;
  end;
end;

mandoza 27. Apr 2018 11:10

AW: Update/Delete Record items in a Stream
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1400738)
And then your next steps will probably be writing
Delphi-Quellcode:
UpdateFirstName
or
Delphi-Quellcode:
UpdateId
methods.

Why not
  1. Load your data from stream
  2. Alter them in memory
  3. Save everything again

It looks like you already have everything you need.

How to do that please ?

mandoza 27. Apr 2018 11:41

AW: Update/Delete Record items in a Stream
 
what i wanted is to be able to :
1- Load that record item from Stream ( Done )
2- Update that record item value ( Done )
3- Update that modified record item value in the Stream , that means Replace that OLD item value with this New Value then save again ( HOW TO )

Der schöne Günther 27. Apr 2018 11:47

AW: Update/Delete Record items in a Stream
 
Can you post your real code? Your current
Delphi-Quellcode:
LoadFromStream()
and
Delphi-Quellcode:
SaveToStream()
appear to be testing routines because they contain syntax errors and, in their current form, do not make sense:

In
Delphi-Quellcode:
LoadFromStream()
you load your data into a local variable
Delphi-Quellcode:
myRec
. After that, your procedure ends and
Delphi-Quellcode:
myRec
is gone. You can inspect
Delphi-Quellcode:
myRec
in the debugger, but in the end, this method accomplishes nothing.

"Real code" would, for example, take an instance of
Delphi-Quellcode:
TMyrecord
and a filename as parameters for
Delphi-Quellcode:
LoadFromStream()
and
Delphi-Quellcode:
SaveToStream()

mandoza 27. Apr 2018 11:55

AW: Update/Delete Record items in a Stream
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1400748)
Can you post your real code? Your current
Delphi-Quellcode:
LoadFromStream()
and
Delphi-Quellcode:
SaveToStream()
appear to be testing routines because they contain syntax errors and, in their current form, do not make sense:

In
Delphi-Quellcode:
LoadFromStream()
you load your data into a local variable
Delphi-Quellcode:
myRec
. After that, your procedure ends and
Delphi-Quellcode:
myRec
is gone. You can inspect
Delphi-Quellcode:
myRec
in the debugger, but in the end, this method accomplishes nothing.

"Real code" would, for example, take an instance of
Delphi-Quellcode:
TMyrecord
and a filename as parameters for
Delphi-Quellcode:
LoadFromStream()
and
Delphi-Quellcode:
SaveToStream()

Thank you , i don't have an issue with
Delphi-Quellcode:
LoadFromStream()
and
Delphi-Quellcode:
SaveToStream()
, because i'll use my Rec else where , my issue now is with updating MyRec value then save it again ( at same stream position ) . thank you again

Delphi-Quellcode:
procedure UpdateMyRecLastName(NewLastName:string);
var
  LoadingStream: TFileStream;
  i, j: Integer;
  myRec:Tmyrecord;
begin
  SavingStream := TFileStream.Create('SAVE.test', fmCreate or fmOpenWrite or fmShareDenyWrite);
  try
       myRec.LastName := ReadStringFromStream(SavingStream); // load the OLD value
       myRec.LastName:=NewLastName; // update it
     
      {
     Here how to update myRec.LastName value in the LoadingStream
      }
   
  finally
    SavingStream.Free;
  end;
end;

Der schöne Günther 27. Apr 2018 13:43

AW: Update/Delete Record items in a Stream
 
I still don't understand the problem. Let's assume you change your local variable myRec to be a parameter of both LoadFromStream(..) and SaveToStream(..). They both now look like this:

Delphi-Quellcode:
Procedure SaveToStream(const myRec:Tmyrecord);
var
  SavingStream: TFileStream;
begin
  SavingStream := TFileStream.Create('SAVE.test', fmCreate or fmOpenWrite or fmShareDenyWrite);
  try
     WriteStringToStream(SavingStream, myRec.Firstname);
     WriteStringToStream(SavingStream, myRec.LastName);
     WriteIntegerToStream(SavingStream, myRec.ID);
  finally
   SavingStream.Free;
  end;
end;

procedure LoadFromStream(var myRec: TMyRecord);
var
  LoadingStream: TFileStream;
begin
  LoadingStream := TFileStream.Create('SAVE.test', fmOpenRead or fmShareDenyWrite);
  try

     myRec.Firstname:= ReadStringFromStream(LoadingStream);
      myRec.LastName := ReadStringFromStream(LoadingStream);
     myRec.ID := ReadIntegerFromStream(LoadingStream);

  finally
   LoadingStream.Free;
  end;
end;
If you want to change the LastName field of some file from disk, then all you need to do is

Delphi-Quellcode:
var
   myRec: Tmyrecord;
begin
   LoadFromStream(myRec);
   myRec.LastName := 'Wombat';
   SaveToStream(myRec);
end;
The old file ("save.test") will get overwritten and the file will only contain the data stored in
Delphi-Quellcode:
myRec
.

mandoza 27. Apr 2018 13:51

AW: Update/Delete Record items in a Stream
 
many thanks for your great efforts , is there any other alternative to load just one field say :

Delphi-Quellcode:
myRec.LastName := ReadStringFromStream(LoadingStream);
let's update
Delphi-Quellcode:
myRec.LastName
with a new value
Delphi-Quellcode:
myRec.LastName := 'Wombat';
// to change
not the whole record

then save only this new field value , something like update then save whithout loading the whole record just the target one .
I hope you get me now .

like this step :
https://www.thoughtco.com/create-dat...-files-1058003

please read the "Change and Update" part

Der schöne Günther 27. Apr 2018 17:00

AW: Update/Delete Record items in a Stream
 
I do get you, and I understand what you want. It's just my personal opinion: I believe there is absolutely no use of putting this much work into all of this when everything you need is already there. It makes no sense to not read and write a few bytes.

Anyway :), the part above ("Seeking and Positioning") is important as well. When you only want to read the LastName part of your file, you will have to skip the
Delphi-Quellcode:
FirstName
part. Since you do not know how many bytes are used to store the FirstName, you will first have to read the length of FirstName, then use Stream.Seek(..) from your current position to reach the part where the LastName string is stored.

I'll give you two examples:
The easiest one first:
Delphi-Quellcode:
function loadLastNameFromStream_EASY(): String;
var
   LoadingStream: TFileStream;
begin
   LoadingStream := TFileStream.Create('SAVE.test', fmOpenRead or fmShareDenyWrite);
   try
      // read it, but do nothing with it
      ReadStringFromStream(LoadingStream);
      Result := ReadStringFromStream(LoadingStream);
   finally
      LoadingStream.Destroy();
   end;
end;

The harder part is essentially the same. The "advantage" is not reading the UTF8 text bytes of
Delphi-Quellcode:
FirstName
. The disadvantage is that you end up more complicated code:
Delphi-Quellcode:
function loadLastNameFromStream_HARDER(): String;
var
   LoadingStream: TFileStream;
   stringLength: Integer;
begin
   LoadingStream := TFileStream.Create('SAVE.test', fmOpenRead or fmShareDenyWrite);
   try
      // determine length of FirstName
      LoadingStream.ReadBuffer(stringLength, SizeOf(stringLength));
      // skip
      LoadingStream.Seek(stringLength, TSeekOrigin.soCurrent);
      // we have now reached the position where "LastName" is stored, proceed as usual
      Result := ReadStringFromStream(LoadingStream);
   finally
      LoadingStream.Destroy();
   end;
end;

Once again: I believe you're taking the wrong approach. We have now a way of just reading the "LastName" part from a file. The way of writing the LastName is much more complicated: You can't just put a few additional bytes into a file and keep the rest as it is. When you change the LastName to something longer, you will also overwrite the ID part in your file. If you change the name to something shorter, garbage data will remain between the end of
Delphi-Quellcode:
LastName
and
Delphi-Quellcode:
ID
.
To avoid corruption, you will first have to read everything that comes after LastName, then seek back to where LastName starts, then write your LastName part, and then write everything else you previously read in. Then, in case your new name is shorter, truncate the file so no garbage data remains at the end.

Is all of this worth the hassle? No, it's not.
You have a handy method of reading a TMyRecord, and writing one as well. Just use them and use all the time saved for something nice. :wink:

mandoza 28. Apr 2018 09:13

AW: Update/Delete Record items in a Stream
 
So many thanks for all your great efforts .


Alle Zeitangaben in WEZ +1. Es ist jetzt 07:38 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