Solved

Record problem

Posted on 2002-06-04
11
191 Views
Last Modified: 2010-04-04
I'm working with a typed file which stores records of:

type TFileRec = packed record
  FileName: string[20];
  Path: string[70];
end;

my question is so easy it scares me. I've seen many ways to append records, and navigate through them (go to next, go to last, etc.), now HOW CAN I DELETE ONE OF THESE RECORDS?

please use TFileStream to do the deletion.

THANKS! in advance
0
Comment
Question by:pin_plunder
  • 4
  • 2
  • 2
  • +3
11 Comments
 
LVL 14

Expert Comment

by:DragonSlayer
ID: 7055413
The easiest way would be to locate which record needs to be deleted (e.g. Record 40), then stream out everything from Record 40 onwards out to another variable (another stream or another buffer). Then truncate your current file stream at Record 39, and append the data again from your buffer.
0
 
LVL 1

Expert Comment

by:bnemmers
ID: 7055444
If I understand your question correctly what you need is an old simple link list
In your records you need a pointers to the prior record and pointer to the next record.
If you need to delete a record somewhere in the middle than

1)     First store the value of the pointers for the next record and prior record, of the one you need to delete.
2)     Free the memory that the pointer points to
3)     Set the pointer value of the prior records “next record pointer” to equal the saved value of the next record.
4)     Set the pointer value of the next records “prior record pointer” to equal the saved value of the prior record

Good luck Bill :-)
0
 
LVL 11

Expert Comment

by:robert_marquardt
ID: 7055514
He speaks of a file.

Welcome to the wonderful world of ISAM files.
The usual trick is the one memory management uses.
Keep a list (of offsets) of the allcated and a list of the free records in your file.
Deleting means moving the offset from the allocated list to the free list. Allocating goes the other way round.
If you run out of free records then add some to the record file and add their offsets to the free list.
0
 
LVL 27

Expert Comment

by:kretzschmar
ID: 7055627
from my paq

i've here a simple sample for a recordbased flatfile,
maybe you can adapt somewhat

unit r_file_u;

interface

uses
 Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
 StdCtrls;

type
 TForm1 = class(TForm)
   BInsert: TButton;
   BUpdate: TButton;
   Edit1: TEdit;
   Edit2: TEdit;
   BPrev: TButton;
   BNext: TButton;
   BDelete: TButton;
   procedure FormCreate(Sender: TObject);
   procedure BInsertClick(Sender: TObject);
   procedure BUpdateClick(Sender: TObject);
   procedure BDeleteClick(Sender: TObject);
   procedure BNextClick(Sender: TObject);
   procedure BPrevClick(Sender: TObject);
   procedure FormClose(Sender: TObject; var Action: TCloseAction);
 private
   { Private-Deklarationen }
 public
   { Public-Deklarationen }
 end;

var
 Form1: TForm1;

Type TheRec = Record  //a record
               Name1 : String[100];
               Name2 : String[100];
             end;
Var
 f : file of TheRec;    // file of this record  
 r : TheRec;   {Buffer} // a memorybuffer for this record
 cr : LongInt; {FileCursor}  //Points to the current record in the file
 rc : LongInt; {RecordCount} //Holds the amount of records in the file

implementation

{$R *.DFM}

//open the file
procedure TForm1.FormCreate(Sender: TObject);
begin
 assignfile(f,'MyFile.Dat');  //we will work with this file
 if not(fileexists('MyFile.Dat')) then  //if this file not exists
   rewrite(f)  //create this file
 else
   reset(f);   //open this file
 cr := 0;      //
 rc := fileSize(f);
end;

procedure TForm1.BInsertClick(Sender: TObject);
begin
 r.name1 := edit1.Text;
 r.name2 := edit2.Text;
 seek(f,rc);
 write(f,r);
 inc(rc);
 cr := rc;
end;

procedure TForm1.BUpdateClick(Sender: TObject);
begin
 if cr > 0 then
 begin
   r.name1 := edit1.text;
   r.name2 := edit2.text;
   seek(f,cr-1);
   write(f,r);
 end;
end;

procedure TForm1.BDeleteClick(Sender: TObject);
begin
 if rc > 0 then      {Are there record in}
 begin
   if cr <> rc then  {CurrentCursor is not LastRecord }
   begin
     seek(f,rc-1);   {Goto LastRecord}
     read(f,r);      {Buffer LastRecord}
     seek(f,cr-1);   {Goto Record to deleted}
     write(f,r);     {Paste over LastRecord}
   end
   else              {The Record to delete is LastRecord}
   begin
     if rc > 1 then    {More then one Record}
     begin
       seek(f,rc - 2); {Goto the Record before LastRecord}
       read(f,r);      {Read for Output}
       cr := rc - 1;   {set CurrentCursor to Record before LastRecord}
     end
     else
     begin             {Last Record will be deleted : File then empty }
       r.Name1 := '';
       r.Name2 := '';
       cr := 0;
     end;
   end;
   seek(f,rc-1);     {Goto LastRecord}
   truncate(f);      {Truncate the file here}
   edit1.text := r.name1;  {Output }
   edit2.text := r.Name2;
   dec(rc);
 end;
end;

procedure TForm1.BNextClick(Sender: TObject);
begin
 if cr < rc then
 begin
   read(f,r);
   inc(cr);
   edit1.text := r.name1;
   edit2.text := r.Name2;
 end;
end;

procedure TForm1.BPrevClick(Sender: TObject);
begin
 if cr > 1 then
 begin
   seek(f,cr-2);
   read(f,r);
   dec(cr);
   edit1.text := r.name1;
   edit2.text := r.Name2;
 end;

end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
 closefile(f);
end;

end.

meikl ;-)
0
 
LVL 4

Expert Comment

by:nestorua
ID: 7056918
HI,
Create MemoryStream, read all the records of your FileStream
from the beginning to the preceding to the record to be deleted, then read all the records beginning right after the
record to be deleted to the end of the FileStream, save the MemoryStream to the disk.
Sincerely,
Nestorua.
0
Threat Intelligence Starter Resources

Integrating threat intelligence can be challenging, and not all companies are ready. These resources can help you build awareness and prepare for defense.

 

Author Comment

by:pin_plunder
ID: 7057089
dear kretzschmar your BDeleteClick procedure doesn't really delete the record, it just fills it with blank strings, which I also thought would work, but I'm afraid to say it doesn't, at least if the record is one in the middle of the list.

DragonSlayer, that's exactly what I was trying to do. But I get a fatal error.


var
  tempstr: string;
begin
  FRecStream.Seek(SizeOf(TFileRec) * (cmbFileName.ItemIndex + 1), soFromBeginning);
  FRecStream.Read(tempstr, FRecStream.Size - FRecStream.Position); // this line gets the error
  FRecStream.Seek(SizeOf(TFileRec) * cmbFileName.ItemIndex, soFromBeginning);
  FRecStream.Write(tempstr, SizeOf(TFileRec) * (cmbFileName.ItemIndex + 1));
end;

{ Note: FRecStream is a global variable of type TRecordStream, a class I made which descends from TFileStream, what I'm trying to do is to add a DeleteRec method which gets RecNumber as parameter and deletes that record }

The problem here is with tempstr (I think) because of it's size. I'm I right? I would like a complete example of how to do this deletion DragonSlayer, if it's not too much trouble for you. THANKS :)

if tempstr is shortstring everything "works", but not the way I like it, because shortstrings end when they found the end of line character. Is there any way to make this work using tempstr of type string.

thanks all for your comments

pablo.
0
 
LVL 27

Expert Comment

by:kretzschmar
ID: 7057546
then your last record is an empty record,

->
review the code you will see that the last record is copied over the to delete record, then the file will be truncated before the last record

meikl ;-)
0
 
LVL 14

Expert Comment

by:DragonSlayer
ID: 7058308
Here's the full code in a working programme:

unit uMain;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ComCtrls, ActnList;

type
  TFileRec = packed record
    FileName: string[20];
    Path: string[70];
  end;

  TfrmStreamTest = class(TForm)
    Label1: TLabel;
    Label2: TLabel;
    edtFileName: TEdit;
    edtPath: TEdit;
    ListView1: TListView;
    Button1: TButton;
    Button2: TButton;
    ActionList1: TActionList;
    actAppend: TAction;
    actDelete: TAction;
    procedure actDeleteUpdate(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure actDeleteExecute(Sender: TObject);
    procedure actAppendUpdate(Sender: TObject);
    procedure actAppendExecute(Sender: TObject);
    procedure FormShow(Sender: TObject);
  private
    { Private declarations }
    FRecStream: TFileStream;

    procedure RefreshListView;
    procedure RemoveEntry(AIndex: Integer);
  public
    { Public declarations }
  end;

var
  frmStreamTest: TfrmStreamTest;

implementation

const
  sFileName = 'Recs.dat';
var
  sPath: string;

{$R *.DFM}

procedure TfrmStreamTest.actDeleteUpdate(Sender: TObject);
begin
  actDelete.Enabled := ListView1.Selected <> nil;
end;

procedure TfrmStreamTest.FormCreate(Sender: TObject);
begin
  sPath := ExtractFilePath(Application.ExeName);
  try
    FRecStream := TFileStream.Create(sPath + sFileName,
      fmOpenReadWrite or fmShareCompat);
  except
    // on error, create a new file
    FRecStream := TFileStream.Create(sPath + sFileName,
      fmCreate or fmShareCompat);
  end;
end;

procedure TfrmStreamTest.FormDestroy(Sender: TObject);
begin
  FRecStream.Free;
end;

procedure TfrmStreamTest.RefreshListView;
var
  ListItem: TListItem;
  i, RecCount: Integer;
  FileRec: TFileRec;
begin
  ListView1.Items.Clear;
  if FRecStream.Size > 0 then
  begin
    if (FRecStream.Size mod SizeOf(TFileRec)) <> 0 then
    begin
      MessageDlg('Corrupted file!',
        mtError, [mbOk], 0);
      Exit;
    end;
    RecCount := FRecStream.Size div SizeOf(TFileRec);
    FRecStream.Seek(0, soFromBeginning);
    for i := 0 to RecCount - 1 do
    begin
      FRecStream.Read(FileRec, SizeOf(FileRec));
      ListItem := ListView1.Items.Add;
      ListItem.Caption := FileRec.FileName;
      ListItem.SubItems.Add(FileRec.Path);
    end;
  end;
end;



procedure TfrmStreamTest.actDeleteExecute(Sender: TObject);
begin
  if MessageDlg('Remove entry "' + ListView1.Selected.Caption + '"?',
    mtConfirmation, [mbYes, mbNo], 0) = mrNo then
    Exit;
  RemoveEntry(ListView1.Selected.Index);
  RefreshListView;
end;

procedure TfrmStreamTest.actAppendUpdate(Sender: TObject);
begin
  actAppend.Enabled := (Trim(edtFileName.Text) <> '') and
    (Trim(edtPath.Text) <> '');
end;

procedure TfrmStreamTest.actAppendExecute(Sender: TObject);
var
  FileRec: TFileRec;
begin
  FileRec.FileName := edtFileName.Text;
  FileRec.Path := edtPath.Text;
  FRecStream.Write(FileRec, SizeOf(FileRec));
  RefreshListView;
end;

procedure TfrmStreamTest.RemoveEntry(AIndex: Integer);
var
  sTmpMem: TMemoryStream;
  iTotalRecs: Integer;
  iRecSize: Integer;
begin
  iRecSize := SizeOf(TFileRec);
  // if it's not the last item, then we need to do swapping out
  if FRecStream.Size <> ((AIndex + 1) * iRecSize) then
  begin
    iTotalRecs := FRecStream.Size div iRecSize;
    sTmpMem := TMemoryStream.Create;
    try
      // get the record *after*
      FRecStream.Seek((AIndex + 1 )* iRecSize, soFromBeginning);
      // swap it out
      sTmpMem.CopyFrom(FRecStream, (iTotalRecs - AIndex - 1) * iRecSize);
      // write it back
      FRecStream.Seek(AIndex * iRecSize, soFromBeginning);
      FRecStream.CopyFrom(sTmpMem, 0);
    finally
      sTmpMem.Free;
    end;
  end;
  // truncate
  FRecStream.Size := FRecStream.Size - iRecSize;
end;

procedure TfrmStreamTest.FormShow(Sender: TObject);
begin
  RefreshListView;
end;

end.
0
 
LVL 14

Accepted Solution

by:
DragonSlayer earned 50 total points
ID: 7058311
And here's the form (uMain.dfm)

object frmStreamTest: TfrmStreamTest
  Left = 260
  Top = 196
  BorderStyle = bsDialog
  Caption = 'Stream Test'
  ClientHeight = 305
  ClientWidth = 446
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  Position = poDesktopCenter
  OnCreate = FormCreate
  OnDestroy = FormDestroy
  OnShow = FormShow
  PixelsPerInch = 96
  TextHeight = 13
  object Label1: TLabel
    Left = 16
    Top = 20
    Width = 43
    Height = 13
    Caption = 'FileName'
  end
  object Label2: TLabel
    Left = 16
    Top = 51
    Width = 22
    Height = 13
    Caption = 'Path'
  end
  object edtFileName: TEdit
    Left = 88
    Top = 16
    Width = 250
    Height = 21
    MaxLength = 20
    TabOrder = 0
  end
  object edtPath: TEdit
    Left = 88
    Top = 47
    Width = 250
    Height = 21
    MaxLength = 70
    TabOrder = 1
  end
  object ListView1: TListView
    Left = 16
    Top = 96
    Width = 321
    Height = 193
    Columns = <
      item
        Caption = 'FileName'
        Width = 100
      end
      item
        Caption = 'Path'
        Width = 200
      end>
    ReadOnly = True
    RowSelect = True
    TabOrder = 2
    ViewStyle = vsReport
  end
  object Button1: TButton
    Left = 360
    Top = 16
    Width = 75
    Height = 25
    Action = actAppend
    Default = True
    TabOrder = 3
  end
  object Button2: TButton
    Left = 360
    Top = 96
    Width = 75
    Height = 25
    Action = actDelete
    TabOrder = 4
  end
  object ActionList1: TActionList
    Left = 408
    Top = 184
    object actAppend: TAction
      Caption = 'Append'
      OnExecute = actAppendExecute
      OnUpdate = actAppendUpdate
    end
    object actDelete: TAction
      Caption = 'Delete'
      OnExecute = actDeleteExecute
      OnUpdate = actDeleteUpdate
    end
  end
end
0
 

Author Comment

by:pin_plunder
ID: 7065505
your answer was GREAT!, thanks DragonSlayer
0
 
LVL 14

Expert Comment

by:DragonSlayer
ID: 7066141
no probs... glad to help :)
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

Introduction The parallel port is a very commonly known port, it was widely used to connect a printer to the PC, if you look at the back of your computer, for those who don't have newer computers, there will be a port with 25 pins and a small print…
In my programming career I have only very rarely run into situations where operator overloading would be of any use in my work.  Normally those situations involved math with either overly large numbers (hundreds of thousands of digits or accuracy re…
Internet Business Fax to Email Made Easy - With eFax Corporate (http://www.enterprise.efax.com), you'll receive a dedicated online fax number, which is used the same way as a typical analog fax number. You'll receive secure faxes in your email, fr…
Here's a very brief overview of the methods PRTG Network Monitor (https://www.paessler.com/prtg) offers for monitoring bandwidth, to help you decide which methods you´d like to investigate in more detail.  The methods are covered in more detail in o…

744 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now