Solved

Record problem

Posted on 2002-06-04
11
217 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
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
 

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

Free Tool: IP Lookup

Get more info about an IP address or domain name, such as organization, abuse contacts and geolocation.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

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…
Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
Come and listen to Percona CEO Peter Zaitsev discuss what’s new in Percona open source software, including Percona Server for MySQL (https://www.percona.com/software/mysql-database/percona-server) and MongoDB (https://www.percona.com/software/mongo-…
There are cases when e.g. an IT administrator wants to have full access and view into selected mailboxes on Exchange server, directly from his own email account in Outlook or Outlook Web Access. This proves useful when for example administrator want…

705 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