在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
文件是同一类型元素的有序集合,是内存与外设间传输数据的渠道。一些外设如显示器、键盘、打印机等都可以看作文件,但最常用的还是磁盘文件,这也是本章我们主要讨论的对象。 Delphi继承了Object Pascal的文件管理功能,并有很大的发展,其中最主要的是提供了用于文件管理的标准控件,同时也提供了更多的文件管理函数。利用Delphi的强大功能,开发一个自己的文件管理系统就成为很容易的事。 本章首先介绍Delphi文件管理的基本概念和标准过程/函数,并提供了一个记录文件的应用实例,这是从我们实际课题开发中提取出来的。而后介绍Delphi提供的文件控件的使用方法。最后提供的一个综合例程MDI文件管理器则是对Delphi文件管理功能的综合应用。
6.1 文件类型和标准过程 Delphi同Object Pascal一样支持三种文件类型,即:文本文件、记录文件、无类型文件。 6.1.1文本文件 文本文件类型的变量用如下方法声明: var TextFileVar: Text ; 文本文件是以行为单位进行读、写操作的。由于每一行长度不一定相同,不能计算出给定行在文件中的确切位置,因而只能顺序地读写。而且文本文件只能单独为读或写而打开,在一个打开的文本文件上同时进行读、写操作是不允许的。 6.1.1.1 文本文件的打开、关闭 文本文件的打开需要两个步骤:(1). 文件变量与文件名关联;(2). 初始化读写。 联文件变量与文件名调用AssignFile标准过程: AssignFile ( TextFileVar , FileName ) ; FileName 既可以是全路径名,也可以仅是文件名。对于后者系统将在当前目录下查找。 AssignFile是Delphi新提供的一个函数,其功能等价于Object Pascal中的Assign。而Assign在Delphi中更多地被用作一个方法名。 初始化读写有三种方式: 1. Reset : 为读打开文件并把文件指针移动到文件首; 2. Rewrite : 为写创建一个新文件; 3. Append : 为写打开存在的文件并把文件指针定位在文件尾。 当使用Reset或Append过程而文件不存在时将会引发一个I/O异常。有关I/O异常的处理请参看本章例程和第十二章中的介绍。 文件的关闭很简单,只须调用CloseFile过程即可。 虽然Delphi应用程序在退出时会自动关闭所有打开的文件,但自己动手关闭文件可以确保释放文件句柄,并使程序的可移植性增强。 为保持兼容,Delphi也允许用户用Assign建立关联,Close关闭文件。 6.1.1.2 文本文件的读写 从文本文件中读取信息用Read、Readln两个标准过程。 当读入数值时,Read、Readln假定数值是用一个或多个空格分开的,而不是逗号、分号或其它字符。对如下一条语句: Read ( TextFileVar , Num1 , Num2 , Num3 ) ; 如果文件中的数值是: 100 200 300 则能够成功读入,而若文件中的数值是 100 200, 300 则Read读入“200,”并试图把它转化成一个数值时会引发一个异常。 当读入字符是字符串时,Read、Readln过程总是读取尽可能多的字符填充到字符串变量中或一直读到行结束符为止。因此从文本文件中读取格式化的字符串数据,必须声明与其长度相匹配的字符串变量。如果要从文件中读取单词,必须先把文件中的每一行读入字符串,然后再从字符串中逐个分析出单词。或者一次只从文本文件中读入一个字符并测试每个字符后是否是单词断开处。 格式化字符串之间的分隔符应读入到一个临时变量中,而字符串与数值、数值与数值间的分隔符读入时会自动识别剔除。对如下一行数据: Mon 12:10 40 50 定义 var Day: string[3] ; Time: string[5] ; Num1, Num2: Integer ; 则须用如下的read 语句读入: read ( TextFileVar , Day , c , Time , Num1 , Num2 ) ; C为一个临时字符变量。 6.1.1.3 文本文件的编辑 在Delphi中实现对一个文本文件的编辑,只须让其与一个Tmemo控件建立关联即可: Memo1.Lines.LoadFromFile ( TextFileName ) ; 这样在TMemo上所做的一切修改当调用Memo部件的SaveToFile方法后都会反映到文件中去。 6.1.2 记录文件 记录文件是一种操作更为灵活的文件类型。它允许同时为读和写打开,而且由于记录文件中每条记录的长度固定,所以可随机存取。 记录文件的类型变量可如下声明: var RecordFileVar: file of RecordType; RecordType是一个自定义的记录类型。 有关记录文件的操作我们将在下一节中结合例程进行讨论。 6.1.3 无类型文件 无类型文件提供了底层的I/O通道,可用于存取可变长度记录的文件。经常用于文件的复制操作中。由于Delphi提供了更好的方法(见第四节),所以无类型文件很少使用。有兴趣的读者可参看BlockRead、BlockWrite两个联机帮助主题。 6.1.4 Delphi的文件管理标准过程 根据功能我们把标准过程划分为十一类进行介绍。 6.1.4.1 文件的打开与关闭 AssignFile :把一个外部文件名和一个文件变量相关联 Reset :打开一个存在的文件 Rewrite :创建并打开一个新文件(或覆盖原有文件) Append :以添加方式打开一个文件(只适用于文本文件) CloseFile : 关闭一个打开的文件 FileOpen :打开一个特定的文件并返回文件句柄 FileCreate :创建一个给定文件名的文件并返回文件句柄 FileClose :关闭一个特定句柄的文件 后边三个文件主要供系统内部使用,在文件复制的编程中也往往会用到。它们操作的对象是文件句柄而不是文件变量。 6.1.4.2 文件定位 Seek :把文件当前位置移到指定部分 FilePos : 返回文件的当前位置 Eoln : 返回行结束标志 EOF : 返回文件结束标志 FileSeek : 改变当前文件指针的位置 Seek与FileSeek的区别是:1. Seek仅用于记录文件;2. FileSeek的参数是文件句柄、偏移量、起始位置。其中起始位置有文件首、当前位置、文件尾三种选择。Seek的参数是文件变量、偏移量,偏移量是从文件首开始定位的。3. FileSeek的偏移量以字节数来计算,而Seek是根据记录号进行移动。 Seek、FilePos仅用于记录文件。但任何文件都可以看作是基于字节的记录文件。下面一段程序表示了它们的用法。 { 该例子的设计界面为一个包含TOpenDialog部件的窗体。} uses Dialogs; var f: file of Byte; size: Longint; S: String; y: Integer; begin if OpenDialog1.Execute then begin AssignFile(f, OpenDialog1.FileName); Reset(f); size := FileSize(f); S := 'File size in bytes: ' + IntToStr(size); y := 10; Canvas.TextOut(5, y, S); y := y + Canvas.TextHeight(S) + 5; S := 'Seeking halfway into file...'; Canvas.TextOut(5, y, S); y := y + Canvas.TextHeight(S) + 5; Seek(f,size div 2); S := 'Position is now ' + IntToStr(FilePos(f)); Canvas.TextOut(5, y, S); CloseFile(f); end; end. 6.1.4.3 文件删除与截断 Erase : 删除一个存在的文件 DeleteFile : 删除一个文件 Truncate :从文件当前位置将文件截断 Erase与DeleteFile的区别是:Erase以文件变量为参数,当文件不能删除时引起一个异常;DeleteFile以文件名为参数,当文件不存在或不能删除时返回False,而并不引起一个异常。 6.1.4.4 文件名操作 Rename :文件更名,以文件变量为操作对象 RenameFile :文件更名,参数为文件的原名和新名 ChangeFileExt :改变文件扩展名 ExpandFileName :返回文件全路径名 ExtractFileExt :返回文件扩展名 ExtractFileName :从全路径名中返回文件名 ExtractFilePath :返回特定文件的路径 6.1.4.5 文件属性 FileGetAttr :返回文件属性 FileSetAttr :设置文件属性 6.1.4.6 文件状态 FileSize :返回文件对象大小 IOResult :返回上一次I/O操作的状态 FileExists :检测文件是否存在 6.1.4.7 文件日期 DateTimeToFileDate :把Delphi日期格式转换为DOS日期格式 FileDateToDateTime :把DOS日期格式转换为Delphi日期格式 FileGetDate :返回文件的DOS日期时间戳 FileSetDate :设置文件的DOS日期时间戳 6.1.4.8 文件读写 Read,Readln :从文本或记录文件中读取变量 Write :将指定变量写入文本或记录文件 Writeln :将指定变量写入文本文件并写入一个行结束标志 FileRead :从一个指定文件中读取变量 FileWrite :向指定文件写入数据 FileRead和FileWrite都是以文件句柄为操作对象,主要供系统内部使用。 6.1.4.9 目录操作 MkDir :创建当前目录的子目录 ChDir :改变当前目录 GetDir :返回特定磁盘的当前目录 RmDir :删除一个空子目录 6.1.4.10 磁盘操作 DiskFree :返回磁盘自由空间 DiskSize :返回特定磁盘的大小 6.1.4.11 文件查找 FileSearch :查找目录中是否存在某一特定文件 FindFirst :在目录中查找与给定文件名(可以包含匹配符)及属性集相匹配的第一个文件 FindNext :返回符合条件的下一个文件 FindClose :中止一个FindFirst / FindNext序列 有关文件管理标准过程/函数的更详细资料,请查阅Delphi相关的Help主题。以上的大部分过程在后面都有应用实例,读者可以从中体会其用法。 在Delphi的联机帮助Help系统中把有关文件的过程/函数分为两个主题:I/O Routine和File_Management Routine。前者大部分以文件变量为操作对象,而后者大部分以文件名或文件句柄为操作对象。这里为了方便读者的使用,我们按功能重新进行了分类。在下一节中主要应用I/O Routine主题下的过程,而在第四节的综合举例中主要应用File_Management Routine主题下的过程。 另外,Windows提供了许多有关文件管理的API函数。虽然在一般情况下,利用Delphi提供的函数已足够解决问题,但有时候仍然需要使用Windows API。在(6.4.4.2)中我们就用到了Windows API函数GetDriveType。有关Windows API函数的情况,请读者参阅相关的资料,这里不再进行介绍。 6.2 记录文件的应用 6.2.1 任务介绍 在这一节,我们开发一个系统安全性综合评估方法管理系统。系统安全性在复杂项目开发中十分重要,但由于牵涉面广因而很难获得客观、全面的评估值。鉴于此我们提出多角度、多侧面评估而后定量集成的思路,并在此基础上提出了多种安全性综合评估方法。每种方法由不同部门进行评估而后把结果汇总、综合。 为此我们定义如下的记录类型: type TNature = (Micro,Macro); {方法性质,分为微观和宏观两类} TMethod = Record Name: string[20]; {方法名} Condition: string[40]; {方法适用条件} Nature: TNature; {方法性质} Result: Real; {方法评估值} end; 用来记录不同方法的信息。 由于不同方法的条件、性质不同,因而对工程开发的不同阶段适用方法集也不同。因此需要根据实际情况对方法集进行管理。我们把每一方法作为一条记录,每一方法集作为一个记录文件。下面讨论系统的实现方法。 6.2.2 设计基本思路 本系统要实现的基本功能是文件的打开、创建、关闭、显示,记录的增加、修改、删除以及结果的综合和显示。为此我们使用了两组按钮分别用于文件和记录的操作, 使用一个StringGrid控件来显示文件内容,使用一个只读编辑框显示结果的综合。 其中各部件的名称、功能如下表所示: 表6.1 主窗口部件的设计 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 部件名称 主要属性 备注 ────────────────────────────────────── RecFileForm BorderStyle=bsDialog 文件打开后把文件名附到窗口标题后 Position=poScreenCenter StringGrid1 大小行数动态确定 HazAttr(编辑框) ReadOnly=True 显示综合结果 OpenButton TabOrder=0 打开一个记录文件,若文件不存在则创建 NewButton Caption='打开' 创建一个记录文件,若文件存在则打开 CloseButton Caption='关闭' 关闭一个已打开的文件 AddButton Caption='增加' 增加一条记录 ModifyButton Caption='修改' 修改一条记录 DeleteButton Caption='删除' 删除一条记录 CalcuButton Caption='计算' 计算最终结果并显示 ExitButton Caption='退出' 系统终止。若当前有打开的文件则先关闭 OpenDialog1 Filter= 选择或输入欲打开的文件 'Record File(*.Rec)|.Rec |Any File(*.*)|*.*' ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 另外,StringGrid1、HazAttr的标题用两个标签框(Label)来显示。 另外我们还需要一个编辑对话框。其中四个编辑框Name、Condition、Nature、 Result分别对应TMethod记录的四个域。 为协调程序运行,我们定义了一组全局变量。各变量的类型、作用如下表。 表6.2 全局变量及其作用 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 变量名 类型 作用 ───────────────────────────────── MethodFile MethodFileType 与当前打开文件相关联的文件变量 FileName string[70] 当前打开文件的文件名 Count Count 当前打开文件的记录总数 CurrentRec Integer 当前处理记录号 FileOpened Boolean 当前是否有文件打开 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 记录文件类型MethodFileType的定义为 type MethodFileType = file of TMethod; 布尔变量FileOpened用于控制文件按钮的使能、变灰,记录按钮的反应以及系统结束时是否需要首先关闭文件。 6.2.3 记录文件的打开和创建 记录文件的打开和创建同文本文件一样也需要关联和初始化两个步骤。同文本文件唯一的不同是不能使用Append过程。 记录文件缺省情况下以读写方式打开,如果想以只读或只写方式打开,则需要修改System单元中定义的变量FileMode的值。 FileMode的取值和意义如下表。 表6.3 FileMode的取值和意义 ━━━━━━━━━━━━━━ 取值 意义 ────────────── 0 只读 1 只写 2 读写 ━━━━━━━━━━━━━━ FileMode是一个全局变量,对它的每次修改都将影响所有Reset的操作,因此在打开自己的文件后应还原它的值。 在本系统中,当用户按下“打开”按钮时,首先弹出一个标准文件打开对话框,要求用户输入或选择文件名。确认后如果该文件名的文件存在,则用Reset打开,若不存在则创建。程序清单如下。 procedure TRecFileForm.OpenButtonClick(Sender: TObject); begin if OpenDialog1.Execute then FileName := OpenDialog1.FileName else exit; AssignFile(MethodFile,Filename); try Reset(MethodFile); FileOpened := True; except On EInOutError do begin try if FileExists(FileName) = False then begin ReWrite(MethodFile); FileOpened := True; end else begin FileOpened := False; MessageDlg('文件不能打开',mtWarning,[mbOK],0); end; except On EInOutError do begin FileOpened := False; MessageDlg('文件不能创建',mtWarning,[mbOK],0); end; end; end; end; if FileOpened = False then exit; Count := FileSize(MethodFile); if Count>0 then ChangeGrid; RecFileForm.Caption := FormCaption+' -- '+FileName; NewButton.Enabled := False; OpenButton.Enabled := False; CloseButton.Enabled := True; end; 首先系统试图用Reset打开一个文件,并置FileOpened为True。如果文件不能打开,则引发一个I/O异常。在异常处理过程中,首先检测文件是否存在。若不存在则创建这个文件。否则是其它原因引发的异常,则把FileOpend重置为False, 并显示信息“文件不能打开”。在文件创建过程中仍可能引发异常,因而在一个嵌套的异常处理中把FileOpened重置为False,并提示信息“文件不能创建”。 有关异常处理的内容请读者参看第十二章。这段程序说明:异常处理机制不仅能使我们的程序更健壮,而且为编程提供了灵活性。 当用户按下“创建”按钮时,系统首先弹出一个标准输入框,要求用户输入文件名,确认后系统首先检测文件是否存在。若存在则直接打开,否则创建一个新文件。打开或创建过程导致异常,则重置FileName和FileOpened两个全局变量。 procedure TRecFileForm.NewButtonClick(Sender: TObject); begin FileName := InputBox('输入框','请输入文件名',''); if FileName = '' then Exit; try AssignFile(MethodFile,FileName); if FileExists(FileName) then begin Reset(MethodFile); Count := FileSize(MethodFile); if Count>0 then ChangeGrid; end else begin Rewrite(MethodFile); count := 0; end; FileOpened := true; Except on EInOutError do begin FileName := ''; FileOpened := False; end; end; if FileOpened then begin NewButton.Enabled := False; OpenButton.Enabled := False; CloseButton.Enabled := True; RecFileForm.Caption := FormCaption+' -- '+FileName; end; end; 当文件打开或创建后,所要做的工作有: ● 若文件非空,则计算文件长度,并用文件内容填充StringGrid1 ● “创建”、“打开”按钮变灰,“关闭”按钮使能 ● 把文件名附到窗口标题后 6.2.4 记录文件的读入和显示 定义一个全局变量Count用来保存文件中的记录个数。当文件装入时: Count := FileSize(MethodFile); 如果Count > 0,则首先确定StringGrid1的高度、行数。为保证StringGrid1不会覆盖窗口下面的编辑框,定义一个常量MaxShow。当Count < MaxShow时,记录可全部显示;当Count >= MaxShow时,StringGrid1自动添加一个滚动棒。为保证滚动棒不覆盖掉显示内容,StringGrid1的宽度应留有余地。 确定StringGrid1高度、行数的代码如下: With StringGrid do if count < MaxShow then Height := DefaultRowHeight * (Count+1)+10 else Height := DefaultRowHeight * MaxShow+10; RowCount := Count+1; end; 而后从文件中逐个读入记录并显示在StringGrid1的相应位置: for i := 1 to Count do begin Read(MethodFile,MethodRec); ShowMethod(MethodRec,i); end; ShowMehtod是一个过程,用来把一条记录填入StringGrid1的一行中。对于Name、Condition域而言,只须直接赋值即可;而对Nature 域需要把枚举类型值转化为对应意义的字符串(0:“微观”,1:“宏观”);而对Result域则需要把数值转化为一定格式的字符串: Str (MethodRec.Result:6:4,ResultStr); StringGrid1.Cells[3,Pos] := ResultStr; 即Result显示域宽为6,其中小数点后位数为4。 6.2.5 增加一条记录 当用户单击“增加”按钮时屏幕将会弹出一个记录编辑模式对话框EditForm。在编辑框中填入合适的内容并按OK键关闭后,相应值写入一个TMethod类型的变量MethodRec中。其中Nature和Result 域需要进行转换。之后增加的记录添加到StringGrid1的显示中。 最后文件定位于尾部,写入当前记录,总记录数加1。 Seek(MethodFile,Count); Write(MethodFile,MethodRec); Count := Count+1; 完整的程序清单如下: procedure TRecFileForm.AddButtonClick(Sender: TObject); var MethodRec: TMethod; Rl: Real; k: Integer; EditForm: TEditForm; begin if FileOpenEd = False then Exit; EditForm := TEditForm.Create(self); if EditForm.ShowModal <> idCancel then begin HazAttr.text := ''; MethodRec.Name := EditForm.MethodName.text; MethodRec.Condition := EditForm.Condition.text; case EditForm.NatureCombo.ItemIndex of 0: MethodRec.Nature := Micro; 1: MethodRec.Nature := Macro ; end; Val(EditForm.Result.text,Rl,k); MethodRec.Result := Rl; with StringGrid1 do begin if Count < MaxShow then Height := Height+DefaultRowHeight; RowCount := RowCount+1; end; ShowMethod(MethodRec,Count+1); seek(MethodFile,Count); write(MethodFile,MethodRec); Count := Count+1; end; end; 6.2.6 修改记录 首先获取当前记录位置: CurrentRec := StringGrid1.Row - 1; 而后打开编辑对话框并显示当前值。修改完毕后,修改结果保存在一个记录中并在StringGrid1中重新显示。 最后修改结果写入文件: Seek(MethodFile,CurrentRec); Write(MethodFile,MethodRec); 完整程序如下: procedure TRecFileForm.ModifyButtonClick(Sender: TObject); var MethodRec: TMethod; Rl: Real; k: Integer; EditForm: TEditForm; begin if FileOpened = False then Exit; EditForm := TEditForm.Create(self); CurrentRec := StringGrid1.Row-1; with EditForm do begin MethodName.text := StringGrid1.Cells[0,CurrentRec+1]; Condition.text := StringGrid1.Cells[1,CurrentRec+1]; if StringGrid1.Cells[2,CurrentRec+1] = '微 观' then NatureCombo.ItemIndex := 0 else NatureCombo.ItemIndex := 1; Result.text := StringGrid1.Cells[3,CurrentRec+1]; if ShowModal <> idCancel then begin HazAttr.text := ''; MethodRec.Name := MethodName.text; MethodRec.Condition := Condition.text; case NatureCombo.ItemIndex of 0: MethodRec.Nature := Micro; 1: MethodRec.Nature := Macro ; end; Val(Result.text,Rl,k); MethodRec.Result := Rl; ShowMethod(MethodRec,CurrentRec+1); seek(MethodFile,CurrentRec); write(MethodFile,MethodRec); end; end; end;
6.2.7 记录的删除、插入、排序 删除一条记录的基本思路是:获取当前记录的位置并把该位置后的记录逐个向前移动。 文件在最后一条记录前截断。 for i:=CurrentRec+1 to Count-1 do begin seek(MethodFile,i); read(MethodFile,MethodRec); seek(MethodFile,i-1); Write(MethodFile,MethodRec); end; Truncate(MethodFile); 为避免误删除,在进行删除操作前弹出一个消息框进行确认。删除后要更新全局变量的值和显示内容:
Count := Count - 1; ChangeGrid; 完整的程序如下:
procedure TRecFileForm.DeleteButtonClick(Sender: TObject); var NewFile: MethodFileType; MethodRec: TMethod; NewFileName: String; i: Integer; begin if FileOpened = False then Exit; CurrentRec := StringGrid1.Row-1; if CurrentRec < 0 then Exit; if MessageDlg('Delete Current Record ?', mtConfirmation, [mbYes, mbNo], 0) = idYes then begin HazAttr.text := ''; for I := CurrentRec+1 to Count-1 do begin seek(MethodFile,i); read(MethodFile,MethodRec); seek(MethodFile,i-1); Write(MethodFile,MethodRec); end; Truncate(MethodFile); Count := Count-1; ChangeGrid; end; end; 这里所显示的删除操作简单明了。但在程序开始设计时我却走了一条弯路,后来发现虽然这种方法用于记录的删除操作显得笨拙、可笑,但却恰恰是记录插入、排序的思想。 这种思想的核心是创建一个新文件保存更新后的内容。若新文件顺利创建,则删除原文件,否则恢复原来的文件。程序清单如下:
procedure TRecFileForm.DeleteButtonClick(Sender: TObject); var NewFile: MethodFileType; MethodRec: TMethod; NewFileName: String; i: Integer; begin if FileOpened = False then Exit; CurrentRec := StringGrid1.Row-1; if CurrentRec < 0 then Exit; if MessageDlg('Delete Current Record ?', mtConfirmation, [mbYes, mbNo], 0) = idYes then begin HazAttr.text := ''; NewFileName := ChangeFileExt(FileName,'.sav'); try AssignFile(NewFile,FileName); ReWrite(NewFile); Except On EInOutError do begin Rename(MethodFile,FileName); Exit; end; end; for i := 1 to Count do if I <> CurrentRec+1 then begin MethodRec := GridToRec(i); Write(NewFile,MethodRec); end; closeFile(MethodFile); try AssignFile(MethodFile,Filename); Reset(MethodFile); except on EInOutError do begin DeleteFile(FileName); AssignFile(MethodFile,NewFileName); Reset(MethodFile); Rename(MethodFile,FileName); Exit; end; DeleteFile(NewFileName); Count:=Count-1; ChangeGrid; end; end; 对于记录插入,方法基本同上。对于排序,可先将关键域读入排序,而后再按排序结果对应的记录号顺序重写文件。
6.2.8 结果综合 对不同方法的评估结果,可按一定的公式进行综合。当用户按下“计算”按钮时,系统进行计算并把综合结果写入HazAttr只读编辑框中。 为保证结果显示的正确性,每次增加、修改、删除操作确认后HazAttr编辑框清空。
6.2.9 编辑对话框的输入检查 当用户单击“增加”或“修改”按钮时系统将弹出一个编辑对话框,让用户输入或修改记录内容。其中的三个编辑框,一个组合列表框分别对应TMethod 的四个域。由于TMethod的Result域必须是[0,1]间的小数,因此当用户按OK键关闭对话框时应进行类型和范围检查。 在VB中我做过同样的工作,那时需要对用户输入的键码逐个进行判断。但这种方法很繁琐、很难做圆满(如不能很好地支持编辑键)。而Object Pascal提供了更好的方法。这种方法的关键就在于它的类型转换函数Val:
procedure Val(Str: String;var V; var Code: Integer); V是由Str转换成的整型或实型数。若字符串非法,则出错位置返至Code;否则置Code为0。字符串非法并不会引发一个转换异常。 如果转换后的数超出了我们的范围,则显式把Code置为-1。最后统一通过检测Code是否为0来判断输入是否合法。 我们把输入检查放在对话框的OnCloseQuery事件处理过程中。如输入非法,则禁止对话框关闭,并将输入焦点置于Result编辑框中。但假如用户按了Cancel按钮,则这种检查是多余的。为此定义一个布尔变量IsCancel,对话框生成时置为False。假如用户按下Cancel,则置为True,此时OnCloseQuery事件不进行输入检查。 对话框的OnCloseQuery事件处理过程的程序清单如下:
procedure TEditForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean); var Res: Real; k: Integer; begin if IsCancel = False then begin val(Result.text,Res,k); if (Res > 1) or (Res < 0) then k := -1; if k <> 0 then begin MessageDlg('非法输入 !',mtWarning,[mbOK],0); Result.text := ''; CanClose := False; Result.SetFocus; end; end; end; 6.2.10 文件和系统的关闭 文件关闭须调用CloseFile过程: CloseFile(MethodFile); 并对系统的状态重新进行设置。 系统关闭时首先检测当前是否有打开的文件。若有则先关闭文件。这在主窗口的OnCloseQuery事件中实现。 实现文件关闭的程序清单如下:
procedure TRecFileForm.CloseButtonClick(Sender: TObject); begin if FileOpened then begin CloseFile(MethodFile); FileOpened := False; ClearGrid; OpenButton.Enabled := True; NewButton.Enabled := True; CloseButton.Enabled := False; RecFileForm.Caption := FormCaption; end; end; 实现系统关闭前检查的程序清单如下:
procedure TRecFileForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean); begin if FileOpened then closeFile(MethodFile); end; 6.2.11 记录文件小结 我们所举的例子虽然简单,但基本覆盖了记录文件操作的主要方面。这里关键问题在于灵活应用Delphi提供的文件管理函数。同时,为了保证程序的健壮性应对异常进行捕获并处理。在数据库应用技术发展的今天,记录文件的重要性也许有所下降,但对象我们这里所处理的简单问题它仍有用武之地。 这里所举的例子一次只能处理一个文件。但读者可以很容易把它改为一个MDI程序。虽然对于这里的实际情况来说,似乎并无必要。
6.3 文件控件的应用 Delphi文件管理的最大特色是提供了一组文件操作控件。利用这些控件我们可以快速开发一个文件名浏览系统。其功能强大与其所需书写代码之少所形成的强烈反差,正是Dephi生命力的体现。
6.3.1 文件控件及其相互关系 Delphi提供的专用文件控件如下表所示。 表6.4 Delphi专用文件控件━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 控件名 功能 ─────────────────────────────────────
DriveComboBox 驱动器组合列表框。用于选择当前驱动器
FileListBox 文件列表框。用于显示当前目录中的文件和选中当前文件
FilterComboBox 文件类型组合列表框。用于选择显示文件的类型
DirectoryOutline 目录树(6.4节专门介绍) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 以上控件前四个在Component Palette(部件选择板)的System页中,DirectoryOutline在Component Palette的Samples页中。 以上文件控件再加上文件编辑框、目录标签框(事实上是一般的编辑框、标签框)就可以构成一个完整的文件操作系统。它们之间的联系几乎不用代码支持,只要设置好相应的属性就可以了。 FileEdit、DirLabel、FileListBox、FileFilterComloList、 DirectoryListBox、DriveComboList六个控件间的属性联系如下: DriveComboList .DirList := DirectoryListBox; DirectoryListBox.DirLabel := DirLabel; DirectoryListBox.FileList := FileListBox; FileFilterComboList.FileList := FileListBox; FileListBox.FileEdit := FileEdit; 以上联系可以在设计时完成。只要打开相应属性的选择列表框进行选择即可。也可以在运行时利用如上的赋值语句建立联系。 文件控件的关键属性基本上都在以上联系中反映出来了。除此之外,FileFilterComboList有一个Filter属性,用来设置组合列表框的选择项;FileListBox 有一个Mask属性,用于设置显示文件的类型,这就允许FileListBox在脱离FileFilterComboList单独应用时仍能根据需要显示特定的文件。在6.4节中我们将应用这一功能。 文件控件的方法、事件基本是从ListBox和ComboBox中继承的。但FileListBox 中有一个ApplyFilePath方法很有用,我们将在后边给出其用法。
6.3.2 文件名浏览查找系统的设计思路 作为文件控件的应用实例,我们开发了一个简单的文件名浏览查找系统。这个系统可用于文件名的显示,把选中的文件写入列表框,并能按文件编辑框中输入的通配符对文件进行查找。 表6.5 部件的设计 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 部件 属性 功能 |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论