如何对记录文件进行操作呢?(100分)

  • 主题发起人 主题发起人 bibcock
  • 开始时间 开始时间
B

bibcock

Unregistered / Unconfirmed
GUEST, unregistred user!
请教各位:
如何按追加方式建立一个记录文件呢?
怎样修改一条记录的某一个域呢?
怎样有选择性的读取任意条记录呢?
请列出完整的代码。谢谢!!
 
先找书看看吧。给你完整的代码你看也只会画葫芦。
 
12.1.2 类型文件的处理
就好像处理表中的记录一样,可以把Object Pascal的数据结构保存到磁盘文件中,也可以将数据从文件中直接读入数据结构中。保存Pascal数据结构的文件称为记录文件。为演示此类文件的使用,声明如下记录结构:
// 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;

注意包含Ansi字符串、变量、类实例、接口或动态数组的记录不能写入类型文件中。假设要在一个文件中保存一个或多个这样的记录。可以采用前面的方法,也可以声明一个下面这种记录来实现:
DataFile:File of TPersonRec;

下列代码用来读取单独一条TPersonRec类型的记录。
var
PersonRec:TPersonRec;
DataFile:File of TPersonRec;
begin
AssignFile(DataFile, 'PersonS.dat');
// Open the file for read access.
Reset(DataFileF);
try
if not Eof(DataFile) then
Read(DataFile,PersonRec);
finally
CloseFile(DataFile); // Close the file when finished.
end;
end;


下列代码用来向文件中追加一条记录:
var
PersonRec:TPersonRec;
DataFile:File of TPersonRec;
begin
AssignFile(DataFile, 'PersonS.dat');
// Open the file for read access.
Reset(DataFileF);
Seek(DataFile,FileSize(DataFile));
try
Write(DataFile,PersonRec);
finally
CloseFile(DataFile); // Close the file when finished.
end;
end;

注意,为使文件指针的位置位于末尾,在写记录前调用了Seek()过程。这种用法在Delphi的在线帮助中有详细的说明,此处不再重复。为讲解类型文件的用法,下面创建一个简单的应用程序。这个程序中以Object Pascal格式保存有关个人的信息。此程序允许用户浏览、增加或编辑其中的记录。我们还将讲述TFileStream的用法,利用它来操纵文件的输入/输出。

1. 声明用于类型文件输入/输出的TFileStream的派生类
TFileStream是一种用来存储非对象内容的流(关于流,将在第2 2章“高级组件技术”中讲述)。我们知道记录本身并不能将自身保存在磁盘或内存中。可以将它转换为对象,然后使这个对象具备存储功能;还可以使用TFileStream的存储功能来保存记录。清单12-5中声明了TPersonRec记录和一个TFileStream的派生类TRecordStream 来处理文件的输入/输出。

清单12-5 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.
在上面程序代码中,首先声明了一个TPersonRec记录。TRecordStream是为TPersonRec进行文件输入/输出而从TFileStream继承下来的。TRecordStream有两个特性: NumRecs用来表示流中记录的数量;CurRecs用来表示流当前查看的记录。GetNumRecs ()是NumRecs属性的访问方法,用来判断流中记录的数量。这个方法实际上利用TStream. Size属性提供的流的字节数除以TPersonRec记录的字节数得到结果。比如当TPersonrec有56个字节,同时Size为162时,则流中有四条记录。但是,注意要保证在记录是紧缩的情况下记录的字节数才为56。因为,为使访问方便,像记录和数组这样的结构化类型在内存中都是按照字或双字排列的。这样会占用掉多于实际需要的磁盘空间。不过,当在声明记录时加上保留字packed时,就可以保证数据是紧凑存储的。如果不用packed声明,将导致GetNumRecs()方法得不到理想的结果。GetCurRec()用于判断哪一条记录是当前记录。它利用TStream.Position的值除以TPersonRec记录的字节数而得到结果。SetCurRec()方法用来将文件指针定位于流中RecNo属性所指的记录的开始位置。SeekRec ()用于将文件指针定位于RecNo和Origin参数所指的位置。此方法可将处于任意位置的文件指针移动。它是利用TStream的Seek()来实现的。TStream.Seek()的用法可查找在线帮助文件“Component Wnites Guide”。
WriteRec用于在当前文件指针所处位置写入TPersonRec的内容,当前位置的原有记录将被覆盖。AppendRec()可将新记录追加至文件末尾。我们利用ReadRec()来从流中读取TPersonRec记录,而后调用Seek()将文件指针定位在此记录的起始位置。这样做是为了能以数据库方式使用TRecordStream,此时的文件指针总是处于当前记录的起始位置(以便查看记录)。First()和Last()分别用来将文件指针定位在文件的开始和末尾。
如果当前文件指针不是在末尾, NextRec()将使指针定位在下一条记录的起始处。
如果当前文件指针不是在第一条记录, PreviousRec()将使指针定位在上一条记录的起始处。

2. 使用TFileStream的派生类处理文件的输入/输出

清单12-6是一个使用TRecordStream对象的应用程序主窗体的源代码。此程序是CD的FileOfRec.dpr项目文件。

清单12-6 FileOfRec.dpr的主窗体代码
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.

图1 2 - 1是本程序的主窗体。

主窗体的实现文件中包含了一个TPersonRec字段和一个TRecord -Stream类。TPersonRec字段保存了当前记录的内容,TRecordStream在窗体的OnCreate事件处理过程中创建。如果文件不存在,则先创建此文件;如果存在,则打开此文件。 ShowCurrentRecord()调用RecordStream.ReadRec()将当前记录从流中读出。RecordStream.ReadRec()方法实现时,首先读出整个记录,然后,再将指针置于记录的起始位置。
本程序的大部分功能在代码注释中都有说明,下面只介绍几个关键的部分。
bmAppendClick()用于向文件增加新记录。
bmUpdateClick()方法用于将窗体上的控件的内容写入文件的当前记录中。
其他方法主要是对文件指针的首、末、前、后操作,以便方便地浏览文件。这个程序演示了如何利用文件的输入/输出功能来实现类型文件的简单数据库操作,并讲述了如何利用TFileStream对象来控制记录的输入和输出。
 
同意楼上。
给你一个例子, 忘了是在哪本书上讲的了

type
Address = record
Lastname: string[20];
Firstname: string[20];
Phone: string[15];
StreetAddress: string[50];
City: string[40];
State: string[2];
ZipCode: string[10];
end;

var
AddressFile: file of Address;
AddressData: Address;
FName: string;
RecSize, CurRec: Longint;

{procedure TForm1.LoadRecord ;
begin
Read(AddressFile,AddressData);
ShowRecord;
end;}

procedure TForm1.SaveRecord;
begin
AddressData.Lastname := Edit1.Text;
AddressData.Firstname := Edit2.Text;
AddressData.Phone := Edit3.Text;
AddressData.StreetAddress := Edit4.Text;
AddressData.City := Edit5.Text;
AddressData.State := Edit6.Text;
AddressData.ZipCode := Edit7.Text;
Write(AddressFile, AddressData);
end;

procedure TForm1.ClearData;
begin
Edit1.Text := '';
Edit2.Text := '';
Edit3.Text := '';
Edit4.Text := '';
Edit5.Text := '';
Edit6.Text := '';
Edit7.Text := '';
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
ClearData;
CurRec := 0;
FName := 'Address.dat';
AssignFile(AddressFile, FName);
RecSize := SizeOf(AddressData);
if FileExists(FName) then
begin
Reset(AddressFile);
if not EOF(AddressFile) then
begin
Read(AddressFile, AddressData);
ShowRecord;
end;
end
else
begin
ClearData;
Rewrite(AddressFile);
end;
end;

procedure TForm1.ButtonNewClick(Sender: TObject);
begin
// if (Edit1.Text<>'')or(Edit2.Text<>'')or(Edit3.Text<>'')or(Edit4.Text<>'')or(Edit5.Text<>'')or(Edit6.Text<>'')or(Edit7.Text<>'') then
begin
CurRec := 0;
repeat
Seek(AddressFile, CurRec);
Read(AddressFile, AddressData);
if (AddressData.Lastname = '') and
(AddressData.Firstname = '') and
(AddressData.Phone = '') and
(AddressData.StreetAddress = '') and
(AddressData.City = '') and
(AddressData.State = '') and
(AddressData.ZipCode = '') then Exit;
CurRec := CurRec + 1;
until EOF(AddressFile);
ClearData;
end;
Seek(AddressFile, CurRec);
SaveRecord;
ShowRecord
end;

procedure TForm1.ButtonPreviousClick(Sender: TObject);
begin
if CurRec - 1 < 0 then
begin
CurRec := 0;
Seek(AddressFile, CurRec);
ShowMessage('This is the begining of the file');
end
else
begin
CurRec := CurRec - 1;
Seek(AddressFile, CurRec);
Read(AddressFile, AddressData);
// Seek(AddressFile,CurRec);
ShowRecord;
end;
end;

procedure TForm1.ButtonNextClick(Sender: TObject);
begin
CurRec := CurRec + 1;
Seek(AddressFile, CurRec);
if not EOF(AddressFile) then
begin
Read(AddressFile, AddressData);
Seek(AddressFile, CurRec);
ShowRecord;
end
else
begin
CurRec := CurRec - 1;
Seek(AddressFile, CurRec);
ShowMessage('This is the end of the file');
end;
end;

procedure TForm1.ShowRecord;
begin
Seek(AddressFile, CurRec);
Read(AddressFile, AddressData);
Form1.Edit1.Text := AddressData.Lastname;
Form1.Edit2.Text := AddressData.Firstname;
Form1.Edit3.Text := AddressData.Phone;
Form1.Edit4.Text := AddressData.StreetAddress;
Form1.Edit5.Text := AddressData.City;
Form1.Edit6.Text := AddressData.State;
Form1.Edit7.Text := AddressData.ZipCode;
RecordNumber.Caption := IntToStr(CurRec);
end;

procedure TForm1.ButtonSaveClick(Sender: TObject);
begin
SaveRecord;
ShowRecord;
end;

procedure TForm1.CloseClick(Sender: TObject);
begin
SaveRecord;
CloseFile(AddressFile);
Application.Terminate;
end;
 
非常感谢xianjun和linsb
但是如果记录不是定长的话,应该怎样处理呢?


 
不定长就不是Delphi 中的记录类型了。
不过也可以处理,你可以象dBase/Foxpro那样处理务注字段,
把定长部分作为记录处理,把不定长的放在另一个文件中。
但定长文件包含不定长部分在另一个文件中的地址(以及长度)。
 
后退
顶部