9.3.1 文件類型
1.文件類型概念
Delphi使用文件類型來讀寫存儲在外部存儲介質上的文件。一個文件變量能夠與任意種類的外部設備建立通信,包括磁盤、打印機、鍵盤、繪圖儀、調制解調器等。
例如,程序運行時可以從磁盤文件中讀取數據,向磁盤文件寫入數據;程序運行結束後,數據仍保存在磁盤文件中,不會丟失。
2.文件類型分類
根據文件中數據元素的數據類型,可將文件類型分為文本文件、類型文件和無類型 文件。
(1)文本文件。
元素類型是字符char,每個元素占用一個字節,以回車換行符表示每行結束。Delphi定義的標准文本文件類型是text或textfile,兩個標識符同義。
(2)類型文件。
元素類型可以是整數、實數或記錄等除文件以外的數據類型。每個元素所占的字節數由元素類型決定。
(3)無類型文件。
以字節byte為單位對文件中的二進制數據元素進行操作,而不管每個字節表示什麼類型的數據元素。
3.文件與數組的區別
文件的定義與數組很像,都是由相同數據類型的數據元素組成的序列。但文件不同於數組,區別如下:
· 數組是由固定多個元素組成,而文件的長度是不定的,隨機的。
· 數組元素總是存放在內存,而文件則往往與外部介質相聯系。
· 數組元素以“數組名[下標]”的形式訪問,而文件則需通過文件變量來訪問。
9.3.2 類型文件操作
1.類型文件定義與變量聲明
類型文件的類型定義格式如下:
type類型文件=file of數據類型;
其中,file、of是關鍵字,“數據類型”是文件的元素類型。例如:
type
intFile=file of integer; //整型文件類型
var
f:file of integer; //f是整型文件變量
文件的元素類型必須是固定長度的數據類型,不能是文件、動態數組、長字符串、指針或包含不固定長度域的記錄類型。例如:
fx:file of intFile; //編譯錯,文件的元素類型不能是文件
Delphi中的字符串類型string,由於要支持長字符串,其大小是不固定的,因此不能作為文件的數據類型,但可以使用短字符串類型ShortString,因為它具有256字節的固定長度。當遇到string帶長度時,Delphi會自動將string轉換為ShortString類型,此時亦可作為文件的元素類型。例如:
var f1:file of ShortString; //ShortString類型為256個字節
f2:file of string[20]; //string[20]為20個字節
f3:file of string; //編譯錯,記錄長度不確定
固定長度的記錄類型可以作為文件的元素類型,但包含不固定長度域的記錄類型則不可以。例如,以下定義的記錄類型phoneEntry可以作為文件的元素類型:
type
phoneEntry = record
name: string[20];
phoneNumber: string[20];
address: string[100];
end;
phoneList = file of phoneEntry;
2.為文件變量指定相應文件
在使用文件變量進行文件操作之前,必須調用AssignFile過程建立文件變量與待操作文件之間的聯系。
AssignFile過程聲明如下:
procedure AssignFile(var f; filename:string);
其中,f為文件變量名,f聲明為無類型的變量參數,聲明為無類型參數是為了與所有文件類型兼容;FileName為文件名字符串,是包括文件名的全路徑名。例如:
AssignFile(f,'D:\Output.dat');
指定了文件變量f與磁盤文件'D:\Output.dat'相關聯,其後對變量f的操作都是針對指定文件的。
3.打開文件
指定文件名後,對文件操作之前,應先打開文件。有以下兩種打開文件方式:
(1)Reset方式。
調用Reset過程打開一個已存在文件,然後可從文件中讀取數據,也可向文件寫入數據。Reset過程聲明如下:
procedure Reset(var f [: File; RecSize: Word]);
打開文件變量f所指定的文件,文件變量指向文件開頭的第一個元素。當指定文件不存在時,產生I/O異常EInOutError。
(2)Rewrite方式。
Rewrite過程聲明如下:
procedure Rewrite(var f:File[; Recsize: Word]);
調用Rewrite過程,系統以f指定的文件名,在磁盤上創建一個空文件並准備寫入數據。如果f指定文件已存在,則該文件將被覆蓋,原有內容丟失。
4.讀入文件
調用Reset打開文件後,可使用Read過程讀取文件內容。過程聲明如下:
procedure Read(var f, v1{, v2});
其中f為文件變量,v1、v2為待輸入的變量名,v1等變量的數據類型是文件的元素 類型。
當讀取整型和實型數據時,文件中的數據元素用空格分隔,且必須符合數據格式,否則將產生I/O錯誤。
5.判斷文件是否結束
在讀取文件之前,必須判斷文件是否結束。只有未到文件結束點,才能讀取數據。Eof函數聲明如下:
function Eof(var f):Boolean;
當文件變量f指向文件尾部,讀入文件結束標記時,表示文件結束,Eof函數返回True,否則返回False。
6.向文件寫入數據
調用Rewrite打開文件後,使用Write過程向文件中寫入數據。過程聲明如下:
procedure Write(var f, v1{, v2});
向文件f中寫入若干個文件元素型變量v1、v2的值。
7.關閉文件
無論是以Reset或Rewrite方式打開文件,在對文件操作完畢後,都必須使用CloseFile過程關閉文件,聲明如下:
procedure CloseFile(var f);
關閉文件後,系統釋放文件使用的資源。
以Rewrite方式打開文件,調用Write過程時,數據先寫入內存緩沖區,只有緩沖區滿或關閉文件時,才把數據真正寫入磁盤中,並寫入文件結束標記。如果寫完數據後不關閉文件,可能造成數據的丟失。
9.3.3 文本文件操作
1.文本文件變量說明
Delphi定義了TextFile類型表示文本文件,它與Pascal語言中的Text類型完全相同。例如:
var f:TextFile; //聲明文本文件變量f
2.文件類型共同的操作
與類型文件一樣,對文本文件進行操作也需要以下幾個步驟:
(1)聲明文件變量f。
(2)調用AssignFile過程為文件變量f指定相關文件。
(3)調用Reset或Rewrite過程打開文件f。
(4)Reset打開文件,當未到文件尾,即Eof(f )返回false時,調用Read(f,i)過程,讀入文件f的一個元素值存放在變量中。
(5)Rewrite打開文件時,調用Write(f,i)過程,將變量i的值寫入文件f中。
(6)關閉指定文件CloseFile(f )。
3.僅用於文本文件的操作
(1)與類型文件定義不同的過程與函數。
對於文本文件,讀、寫等過程和函數的定義與類型文件定義有所不同,聲明如下:
procedure Read([var f: Text;]v1{, v2));
function Eof[(var f:Text)]: boolean;
procedure Write([var f: Text;]p1{, p2});
(2)Append添加方式打開文件。
Append過程聲明如下:
procedure Append(var f: Text);
調用Append過程打開文件,文件變量指向文件尾部,此後寫入的數據添加在文件原有數據之後。如果文件不存在,則產生I/O異常。
(3)Readln按行讀取字符串。
Readln過程聲明如下:
procedure Readln([var f: Text;] v1{, v2});
Readln讀取以回車換行符結束的一行字符串,之後跳過回車換行符,再讀下一行。
(4)判斷行是否結束。
對於文本文件,也可以調用Read(f,c)過程逐個地讀取字符,此時可以使用Eoln函數判斷行是否結束。Eoln函數聲明如下:
function Eoln [(var f: Text) ]: boolean;
當文件變量f讀取回車換行符時,表示一行結束,Eoln函數返回True,否則返回False。
對於文本文件,既可以調用Readln過程按行讀取字符串,也可以調用Read過程逐個讀取字符。需注意讀入回車換行符時的操作,例如,Readln讀完一行數據後,再使用Read讀取字符串將得到空串。
以上所述Append、Writeln、Readln、Eoln例程只對文本文件有效,對類型文件無效。
數據既可以保存在類型文件中,也可以保存在文本文件中。兩種方式各有所長,存於類型文件中,數據隱蔽性較強,但讀取不方便,必須根據要求特別編寫讀入程序;存於文本文件中,可使用記事本等多種工具打開文本文件,不必特別編寫讀入程序,但數據是公開的,無法隱藏。實際應用時,可根據需求有所選擇。
4.標准輸入輸出
由於鍵盤和顯示器是操作系統默認的標准輸入/輸出設備,操作系統對設備的訪問也是基於文件進行的,並且標准輸入/輸出文件都是以字符為基本元素的文本文件。
為此,Delphi定義了兩個默認的文本文件變量Input和Output,用於處理標准的輸入與輸出操作。
Input代表標准輸入文本文件,即鍵盤;Output代表標准輸出文本文件,即顯 示器。
平時我們所說的讀和寫語句,實際上是從Input文件中讀入變量值,或向Output文件寫入指定變量的值,其中省略了默認變量Input和Output的形式。例如,下列兩條語句 等價:
Read(i, j); //從Input文件中讀入變量值,省略Input
Read(input, i, j); //從Input文件中讀入變量值
雖然Input是文本文件,但從Input文件中可以讀入非字符的整數類型等變量值,系統已經自動進行了數據類型的轉換,這一功能是普通文本文件所沒有的。
下列語句兩兩等價:
Readln(i,j); 與 Readln(input, i,j);
Readln(); 與 Readln(input);
Write(i,j); 與 Write(output,i,j);
Writeln(i,j);與 Writeln(output, i,j);
Writeln(); 與 Writeln(output);
9.3.4 無類型文件操作
僅以file聲明的文件變量稱為無類型文件變量。例如:
var f: file;
無類型文件變量f代表二進制字節文件,而不管每個字節表示什麼類型的數據元素。
對無類型文件的讀/寫操作由BlockRead和BlockWrite過程實現,它們以二進制數據塊為單位,通常一個數據塊為128個字節,一次可讀或寫若干數據塊。
9.3.5 文件的隨機操作
除了順序存取,文件還有隨機存取方式,即按記錄位置的編號進行讀/寫操作,通常對記錄式文件采用隨機存取方式,而對文本文件這種字符流式文件則不采用隨機存取方式。
有關隨機存取的操作如下,這些操作僅用於類型文件和無類型文件,不能用於文本 文件。
function FileSize(var f: file): Int64; //返回文件長度,即記錄總數
function FilePos(var f: file): Int64; //返回文件當前記錄編號
procedure Seek(var f: file, n: integer); //跳過n個記錄位置
procedure Truncate(var f: file); //截斷文件
記錄編號通常從0開始計數。給定一個記錄編號,調用Seek(f,n)過程,文件的讀/寫指針將從當前記錄位置處跳過n個記錄,定位在所需的記錄位置處,進行讀/寫操作。調用Truncate(f ) 過程將文件從當前記錄處截斷,從當前記錄至文件尾的若干記錄則被刪除。
采用順序存取方式,通常將讀文件和寫文件的操作分開進行,以Reset( )方式打開文件准備讀,以Rewrite( )方式打開新文件准備寫,讀和寫操作分別進行。
實際上,對於可讀寫的類型文件和無類型文件,以Reset( )和Rewrite( )兩種方式打開文件,既可以讀取數據也可以寫入數據,同時支持隨機存取方式。
以Reset( )方式打開文件時,文件的原有內容仍保留,系統設置的文件指針定位於第一個記錄,記錄號為0。此時,如果寫數據,則將原第一個記錄內容覆蓋。例如:
Reset(f); //當前記錄號為0
Write(f,p); //覆蓋當前記錄內容
如果將文件指針定位於文件尾,則可在原文件之後增加新內容。例如:
Reset(f);
Seek(f,FileSize(f)); //定位到文件尾
Write(f,p); //追加記錄
以Rewrite( )方式打開文件時,文件原有內容不保留,文件指針定位於第一個記錄,記
錄號為0,這是要寫入的記錄位置。寫入若干記錄後,可以改變文件指針,再讀記錄, 例如:
Rewrite(f);
Write(f ,p);
Write(f ,p);
Seek(f,0);
Read(f,p); //讀出第一個記錄
9.3.6 與文件目錄相關的標准過程和函數
Delphi在System、SysUtils等單元中定義了與文件和目錄操作有關的若干過程/函數。
1.System單元中與目錄操作有關的過程/函數
procedure ChDir(const s: string); //改變當前目錄
procedure MkDir(const s: string); //創建一個新的子目錄S
procedure RmDir(const s: string); //刪除一個空的目錄S
function Flush(var t:Text): integer; //清空文本輸出緩沖區
function IOResult: integer; //返回I/O操作狀態。
procedure Move(const Source; var Dest; Count: Integer);
//將源數據塊以字節形式拷貝至目的數據塊
2.SysUtils單元中與文件操作有關的過程/函數
function GetCurrentDir: string; //返回當前目錄路徑名
function SetCurrentDir(const Dir: string): Boolean; //設置當前目錄
function FileCreate(const FileName: string):Integer; //創建文件
function FileExists(const FileName: string): Boolean; //判斷指定文件是否存在
function CreateDir(const Dir: string): Boolean; //創建新目錄
function ExpandFileName(const FileName: string): string;
//從文件名返回完整的文件路徑名
function ExtractFileDir(const FileName: string): string;
//從文件路徑名中返回目錄路徑名
function ExtractFileName(const FileName: string): string;
//從文件路徑名中返回文件名和擴展名
function ExtractFileExt(const FileName: string): string;
//從文件路徑名中返回擴展名
function DeleteFile(const FileName: string): Boolean; //刪除指定文件
function RenameFile(const OldName, NewName: string): Boolean;
//重命名指定文件
function FileGetAttr(const FileName:string):Integer; //返回指定文件的屬性
function DiskSize(Drive: Byte): Int64; //返回指定驅動器的總容量
function DiskFree(Drive: Byte): Int64; //返回指定驅動器的剩余磁盤空間