请问下面这个程序应该如何实现删除数据的功能?(40分)

Z

zqw0117

Unregistered / Unconfirmed
GUEST, unregistred user!
请问下面这个程序应该如何实现删除数据的功能?我说的删除,是直接从记录文
件中去掉不要的数据,而不是清空该数据的所有值。所谓“直接从记录文件中去
掉不要的数据”是指,例如文件中有5条数据,文件大小是10K,那么删除第三条
记录后,文件中的数据总数应该是4,而文件大小应该是8K,这样的功能该如何
做?

注意:我不需要这样的回答“将要删除的数据做出标记,然后读取所有没有该标
记的数据,然后拷贝到另外一个临时文件中,最后用这个临时文件替换原来的数
据文件”。

下面是所有单元的代码:

{***********************************
FileOfRec.dpr}

program FileOfRec;

uses
Forms,
MainFrm in 'MainFrm.pas' {MainForm},
persrec in 'persrec.pas';

{$R *.RES}

begin
Application.CreateForm(TMainForm, MainForm);
Application.Run;
end.


{***********************************
persrec.pas}

unit persrec;

interface
uses Classes, dialogs, sysutils;

type

// Define the record that will hold the person's information.
TPersonRec = packed record
FirstName: String[20];
LastName: String[20];
MI: String[1];
BirthDay: TDateTime;
Age: Integer;
end;

// Create a descendant TFileStream which knows about the TPersonRec

TRecordStream = class(TFileStream)
private
function GetNumRecs: Longint;
function GetCurRec: Longint;
procedure SetCurRec(RecNo: Longint);
protected
function GetRecSize: Longint; virtual;
public
function SeekRec(RecNo: Longint; Origin: Word): Longint;
function WriteRec(const Rec): Longint;
function AppendRec(const Rec): Longint;
function ReadRec(var Rec): Longint;
procedure First;
procedure Last;
procedure NextRec;
procedure PreviousRec;
// NumRecs shows the number of records in the stream
property NumRecs: Longint read GetNumRecs;
// CurRec reflects the current record in the stream
property CurRec: Longint read GetCurRec write SetCurRec;
end;

implementation

function TRecordStream.GetRecSize:Longint;
begin
{ This function returns the size of the record that this stream
knows about (TPersonRec) }
Result := SizeOf(TPersonRec);
end;

function TRecordStream.GetNumRecs: Longint;
begin
// This function returns the number of records in the stream
Result := Size div GetRecSize;
end;

function TRecordStream.GetCurRec: Longint;
begin
{ This function returns the position of the current record. We must
add one to this value because the file pointer is always at the
beginning of the record which is not reflected in the equation:
Position div GetRecSize }
Result := (Position div GetRecSize) + 1;
end;

procedure TRecordStream.SetCurRec(RecNo: Longint);
begin
{ This procedure sets the position to the record in the stream
specified by RecNo. }
if RecNo > 0 then
Position := (RecNo - 1) * GetRecSize
else
Raise Exception.Create('Cannot go beyond beginning of file.');
end;

function TRecordStream.SeekRec(RecNo: Longint; Origin: Word): Longint;
begin
{ This function positions the file pointer to a location
specified by RecNo }

{ NOTE: This method does not contain error handling to determine if this
operation will exceed beyond the beginning/ending of the streamed
file }
Result := Seek(RecNo * GetRecSize, Origin);
end;

function TRecordStream.WriteRec(Const Rec): Longint;
begin
// This function writes the record Rec to the stream
Result := Write(Rec, GetRecSize);
end;

function TRecordStream.AppendRec(Const Rec): Longint;
begin
// This function writes the record Rec to the stream
Seek(0, 2);
Result := Write(Rec, GetRecSize);
end;

function TRecordStream.ReadRec(var Rec): Longint;
begin
{ This function reads the record Rec from the stream and
positions the pointer back to the beginning of the record }
Result := Read(Rec, GetRecSize);
Seek(-GetRecSize, 1);
end;

procedure TRecordStream.First;
begin
{ This function positions the file pointer to the beginning
of the stream }
Seek(0, 0);
end;

procedure TRecordStream.Last;
begin
// This procedure positions the file pointer to the end of the stream
Seek(0, 2);
Seek(-GetRecSize, 1);
end;

procedure TRecordStream.NextRec;
begin
{ This procedure positions the file pointer at the next record
location }

{ Go to the next record as long as it doesn't extend beyond the
end of the file. }
if ((Position + GetRecSize) div GetRecSize) = GetNumRecs then
raise Exception.Create('Cannot read beyond end of file')
else
Seek(GetRecSize, 1);
end;

procedure TRecordStream.PreviousRec;
begin
{ This procedure positions the file pointer to the previous record
in the stream }

{ Call this function as long as we don't extend beyond the
beginning of the file }
if (Position - GetRecSize >= 0) then
Seek(-GetRecSize, 1)
else
Raise Exception.Create('Cannot read beyond beginning of the file.');
end;

end.

{***********************************
MainFrm.pas}

unit MainFrm;

interface

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

const
// Declare the file name as a constant
FName = 'PERSONS.DAT';

type

TMainForm = class(TForm)
edtFirstName: TEdit;
edtLastName: TEdit;
edtMI: TEdit;
meAge: TMaskEdit;
lblFirstName: TLabel;
lblLastName: TLabel;
lblMI: TLabel;
lblBirthDate: TLabel;
lblAge: TLabel;
btnFirst: TButton;
btnNext: TButton;
btnPrev: TButton;
btnLast: TButton;
btnAppend: TButton;
btnUpdate: TButton;
btnClear: TButton;
lblRecNoCap: TLabel;
lblRecNo: TLabel;
lblNumRecsCap: TLabel;
lblNoRecs: TLabel;
dtpBirthDay: TDateTimePicker;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure btnAppendClick(Sender: TObject);
procedure btnUpdateClick(Sender: TObject);
procedure btnFirstClick(Sender: TObject);
procedure btnNextClick(Sender: TObject);
procedure btnLastClick(Sender: TObject);
procedure btnPrevClick(Sender: TObject);
procedure btnClearClick(Sender: TObject);
public
PersonRec: TPersonRec;
RecordStream: TRecordStream;
procedure ShowCurrentRecord;
end;

var
MainForm: TMainForm;

implementation

{$R *.DFM}

procedure TMainForm.FormCreate(Sender: TObject);
begin
{ If the file does not exist, then create it, otherwise, open it for
both read and write access. This is done by instantiating
a TRecordStream }
if FileExists(FName) then
RecordStream := TRecordStream.Create(FName, fmOpenReadWrite)
else
RecordStream := TRecordStream.Create(FName, fmCreate);
end;

procedure TMainForm.FormDestroy(Sender: TObject);
begin
RecordStream.Free; // Free the TRecordStream instance
end;

procedure TMainForm.ShowCurrentRecord;
begin
// Read the current record.
RecordStream.ReadRec(PersonRec);
// Copy the data from the PersonRec to the form's controls
with PersonRec do
begin
edtFirstName.Text := FirstName;
edtLastName.Text := LastName;
edtMI.Text := MI;
dtpBirthDay.Date := BirthDay;
meAge.Text := IntToStr(Age);
end;
// Show the record number and total records on the main form.
lblRecNo.Caption := IntToStr(RecordStream.CurRec);
lblNoRecs.Caption := IntToStr(RecordStream.NumRecs);
end;

procedure TMainForm.FormShow(Sender: TObject);
begin
// Display the current record only if one exists.
if RecordStream.NumRecs <> 0 then
ShowCurrentRecord;
end;


procedure TMainForm.btnAppendClick(Sender: TObject);
begin
// Copy the contents of the form controls to the PersonRec record
with PersonRec do
begin
FirstName := edtFirstName.Text;
LastName := edtLastName.Text;
MI := edtMI.Text;
BirthDay := dtpBirthDay.Date;
Age := StrToInt(meAge.Text);
end;
// Write the new record to the stream
RecordStream.AppendRec(PersonRec);
// Display the current record.
ShowCurrentRecord;
end;

procedure TMainForm.btnUpdateClick(Sender: TObject);
begin
{ Copy the contents of the form controls to the PersonRec and write
it to the stream }
with PersonRec do
begin
FirstName := edtFirstName.Text;
LastName := edtLastName.Text;
MI := edtMI.Text;
BirthDay := dtpBirthDay.Date;
Age := StrToInt(meAge.Text);
end;
RecordStream.WriteRec(PersonRec);
end;

procedure TMainForm.btnFirstClick(Sender: TObject);
begin
{ Go to the first record in the stream and display it as long as
there are records that exist in the stream }
if RecordStream.NumRecs <> 0 then
begin
RecordStream.First;
ShowCurrentRecord;
end;
end;

procedure TMainForm.btnNextClick(Sender: TObject);
begin
// Go to the next record as long as records exist in the stream
if RecordStream.NumRecs <> 0 then
begin
RecordStream.NextRec;
ShowCurrentRecord;
end;
end;

procedure TMainForm.btnLastClick(Sender: TObject);
begin
{ Go to the last record in the stream as long as there are records
in the stream }
if RecordStream.NumRecs <> 0 then
begin
RecordStream.Last;
ShowCurrentRecord;
end;
end;

procedure TMainForm.btnPrevClick(Sender: TObject);
begin
{ Go to the previous record in the stream as long as there are records
in the stream }
if RecordStream.NumRecs <> 0 then
begin
RecordStream.PreviousRec;
ShowCurrentRecord;
end;
end;

procedure TMainForm.btnClearClick(Sender: TObject);
begin
// Clear all controls on the form
edtFirstName.Text := '';
edtLastName.Text := '';
edtMI.Text := '';
meAge.Text := '';
end;

end.

{***********************************
MainFrm.dfm}

object MainForm: TMainForm
Left = 246
Top = 182
Width = 343
Height = 205
Caption = 'Delphi 5 Developer'#39's Guide File of Record'
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = True
OnCreate = FormCreate
OnDestroy = FormDestroy
OnShow = FormShow
PixelsPerInch = 96
TextHeight = 13
object lblFirstName: TLabel
Left = 12
Top = 24
Width = 50
Height = 13
Caption = 'First Name'
end
object lblLastName: TLabel
Left = 148
Top = 24
Width = 51
Height = 13
Caption = 'Last Name'
end
object lblMI: TLabel
Left = 280
Top = 24
Width = 12
Height = 13
Caption = 'MI'
end
object lblBirthDate: TLabel
Left = 12
Top = 68
Width = 89
Height = 13
Caption = 'Birthday mm/dd/yy'
end
object lblAge: TLabel
Left = 148
Top = 68
Width = 19
Height = 13
Caption = 'Age'
end
object lblRecNoCap: TLabel
Left = 12
Top = 0
Width = 58
Height = 13
Caption = 'Record No:'
end
object lblRecNo: TLabel
Left = 72
Top = 0
Width = 6
Height = 13
Caption = '1'
Font.Charset = DEFAULT_CHARSET
Font.Color = clBlue
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
ParentFont = False
end
object lblNumRecsCap: TLabel
Left = 148
Top = 0
Width = 95
Height = 13
Caption = 'Number of Records:'
end
object lblNoRecs: TLabel
Left = 244
Top = 0
Width = 6
Height = 13
Caption = '1'
Font.Charset = DEFAULT_CHARSET
Font.Color = clBlue
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
ParentFont = False
end
object edtFirstName: TEdit
Left = 12
Top = 40
Width = 121
Height = 21
MaxLength = 20
TabOrder = 0
end
object edtLastName: TEdit
Left = 148
Top = 40
Width = 121
Height = 21
MaxLength = 20
TabOrder = 1
end
object edtMI: TEdit
Left = 280
Top = 40
Width = 21
Height = 21
MaxLength = 1
TabOrder = 2
end
object meAge: TMaskEdit
Left = 148
Top = 84
Width = 25
Height = 21
EditMask = '!99;1;_'
MaxLength = 2
TabOrder = 3
Text = ' '
end
object btnFirst: TButton
Left = 12
Top = 112
Width = 75
Height = 25
Caption = 'First'
TabOrder = 4
OnClick = btnFirstClick
end
object btnNext: TButton
Left = 92
Top = 112
Width = 75
Height = 25
Caption = 'Next'
TabOrder = 5
OnClick = btnNextClick
end
object btnPrev: TButton
Left = 172
Top = 112
Width = 75
Height = 25
Caption = 'Prev'
TabOrder = 6
OnClick = btnPrevClick
end
object btnLast: TButton
Left = 252
Top = 112
Width = 75
Height = 25
Caption = 'Last'
TabOrder = 7
OnClick = btnLastClick
end
object btnAppend: TButton
Left = 12
Top = 144
Width = 75
Height = 25
Caption = 'Append Rec'
TabOrder = 8
OnClick = btnAppendClick
end
object btnUpdate: TButton
Left = 92
Top = 144
Width = 75
Height = 25
Caption = 'Update Rec'
TabOrder = 9
OnClick = btnUpdateClick
end
object btnClear: TButton
Left = 172
Top = 144
Width = 75
Height = 25
Caption = 'Clear'
TabOrder = 10
OnClick = btnClearClick
end
object dtpBirthDay: TDateTimePicker
Left = 12
Top = 88
Width = 117
Height = 21
CalAlignment = dtaLeft
Date = 35880.3876936574
Time = 35880.3876936574
DateFormat = dfShort
DateMode = dmComboBox
Kind = dtkDate
ParseInput = False
TabOrder = 11
end
end
 
:zqw0117
看了你的程序,觉得把所有操作都通过filestream,是否效率会有问题?文件操作除了重写似乎还没有直接删除其中一部分的方法。
你能这样做程序吗:用链表读取文件纪录,操作完毕再保存到文件中。
这是通常的方式,不知你为何不能采用?
 
to MikeZ:
是的,我也感觉效率低下,可是我不会“用链表读取文件纪录,操作完毕再保存到文件中。”
的方法,还请您不吝赐教,谢谢!
 
我先把我需要做什么简单的说一下吧。

我需要帮老爸写一个简单的数据库程序,用来记录一些人事记录,例如姓名呀、
年龄呀、岗位呀之类的信息,由于数据量庞大,本来是想用Delphi自己的数据库
写的,但是写好后的打包程序(不包含数据库文件)必须放在一张(最多两张)
软盘中(有一些特殊的原因),而Delphi自己的数据库文件同我写的文件打包的
话,至少得有5M,太大了。后来我又准备用ADO数据库(因为几乎每一台机器上都
有该数据库的引擎),可是(有点不好意思)学艺不精,还不太会写。可老爸要
得很急,我只好自己写数据库引擎了(也就是自己定义数据库引擎,这样也还有
一个好处,就是以后当我学会了ADO数据库后,我也可以很方便的编写函数直接导
入,这样过度起来也不太难)。可是自己写引擎的话,就遇到一个问题(其实还
有很多问题,但这个问题比较迫切),那就是无法快速地删除不要的数据。假如
数据库中的数据总量不是很大,文件大小也不是很大的话,采用标记要删除的数
据,然后将要保留的数据存储到另外一个文件中,最后替换原有文件的方法还是
可以接受的。但是问题就在于,这个数据库可能很大(可能大于50M),要是这么
做的话,岂不是删除一条记录也得花费较长的时间,而且也可能出现磁盘空间不
足,而无法完成操作的问题。我绞尽脑汁也想不出一个快速删除的好方法来。

我只好请教各位大虾了,请帮帮小弟吧,谢谢了。
 
to zqw0117:
给出你的邮箱,我写一段简单的纪录操作程序给你。
但如果你的数据库信息国大的话,建议你还是用数据库管理系统,特别是数据库较复杂的系统。
ADO在delphi中实际上与bde类似(再应用方面),大可不必感到为难!
不过学习一下链表的编程也有好处,我觉得你该补补数据结构的课了
在联系。。。
 
to MikeZ:
谢谢您,我的Email是:zqw0117@sina.com
 
谢谢 MikeZ 老师!
 
顶部