480 likes | 694 Views
第 9 章 檔案入門與處理. 9-1 檔案簡介 9-2 文字檔操作簡介 9-3 二進位檔操作簡介 9-4 隨機存取檔案 9-5 無緩衝區檔案存取操作 9-6 本章綜合練習. 9-1 檔案簡介 . 每個檔案都必須有一個代表它的檔案名稱 (File Name) ,檔名可區分成「主檔名」與「副檔名」,中間以句點 (.) 做分隔,透過這樣的命名方式可以清楚分辨其名稱與類型。如下所示: 主檔名 . 副檔名. 認識資料流.
E N D
第9章 檔案入門與處理 9-1 檔案簡介 9-2 文字檔操作簡介 9-3 二進位檔操作簡介 9-4 隨機存取檔案 9-5 無緩衝區檔案存取操作 9-6 本章綜合練習
9-1檔案簡介 • 每個檔案都必須有一個代表它的檔案名稱(File Name),檔名可區分成「主檔名」與「副檔名」,中間以句點(.)做分隔,透過這樣的命名方式可以清楚分辨其名稱與類型。如下所示: • 主檔名.副檔名
認識資料流 • 資料流(stream)的主要功用是作為程式與周邊的資料傳輸管道,而在C語言中,檔案的處理正是透過資料流(stream)方式存取資料。如下所示:
緩衝區 • 「緩衝區」(buffer),就是在程式執行時,所提供的額外記憶體,可用來暫時存放準備執行的資料。 • 緩衝區的設置是為了存取效率上的考量,因為記憶體的存取速度會比磁碟機來得快速。
未設定緩衝區 • 當使用標準I/O函數時,系統會自動設定緩衝區,並透過資料流來讀寫檔案。 • 所有存取動作都是針對緩衝區,直到關閉檔案才將所有資料寫入磁碟檔案。
檔案的種類 • 文字檔 • 文字檔是以字元編碼的方式進行儲存,在Windows作業系統的記事本(NotePad)程式中則預設以ASCII編碼來儲存文字檔,每個字元佔有1位元組。 • 二進位檔 • 所謂二進位檔,就是以二進位格式儲存,將記憶體中資料原封不動儲存至檔案之中,適用於非字元為主的資料。
檔案存取方式 • 循序式存取(sequential access) • 也就是由上往下,一筆一筆讀取檔案的內容。如果要儲存資料時,則將資料附加在檔案的尾端,這種存取方式常用於文字檔案,而被存取的檔案則稱為循序檔。 • 隨機式存取(random access) • 「隨機存取檔」多半是以二進位檔案為主,會以一個完整單位來進行資料的寫入,這筆單位通常以結構為單位,例如結構中可能包括了一個帳戶的名稱、餘額、投資款項等。
文字檔操作簡介 • C對文字檔的處理方式主要是透過標準I/O函數來進行檔案的開啟、寫入、關閉與設定緩衝區,相關存取函數有fopen()、fclose()、fgets()、fputs()、fprintf()、fscanf()等,都定義在stdio.h標頭檔案中。
檔案的開啟與關閉 • 要進行檔案存取,首先必須開啟資料流,也就是進行開啟檔案的動作。在C中,檔案開啟時必須先透過<stdio.h>中定義的FILE型態建立一個指標變數: • FILE* pf; /*建立用來指向檔案的指標*/ • 當指標變數建立完成之後,才可以透過檔案開啟函數fopen()開啟檔案。fopen()函數定義如下: • FILE* fopen(char* 檔案名稱, char* 開啟模式字串); /*傳回FILE指標*/
檔案名稱 • 包括檔案路徑,如果沒有指定路徑,則會預設為目前的工作目錄。如果要直接指定檔案路徑,則因為反斜線為C中的控制字元,所以必須多加上一個反斜線。 • 例如以下想開啟一個在C:\temp資料夾下的test.txt檔案: • pf = fopen("C:\\temp\\test.txt", "r"); /* 多加上一個反斜線 */
fopen()函數與fclose()函數的宣告示範與練習:CH09_1 • 在Windows系統的記事本程式中建立一個文字檔"test.txt",再利用以下程式範例來練習fopen()函數與fclose()函數的宣告用法。
字元存取函數 • 利用fgetc()函數可從檔案資料流中一次讀取一個字元,然後將讀取游標往下一個字元移動,並且逐步將檔案的內容讀出。fgetc()函數的定義如下: • int fgetc(FILE * 檔案指標); • 如果字元讀取成功,則傳回所讀取字元,否則就傳回EOF(End of File)。另外要判斷檔案是否讀取完畢,可以利用feof()函數來進行檢查。
fgetc()函數與feof()函數的宣告示範與練習:CH09_2fgetc()函數與feof()函數的宣告示範與練習:CH09_2 • 程式範例是簡單開啟一個檔案,並利用fgetc()函數與while迴圈來讀出此檔案中的所有字元資料內容。
fputc()函數寫入檔案的宣告示範與練習:CH09_3 • 程式範例可以讓使用者指定檔案名稱,並利用fputc()函數將使用者鍵盤所輸入內容寫入檔案中,直到按下Enter鍵為止。
字串存取函數 • 在標準I/O函數中的字串存取函數有fgets()函數與fputs()函數兩種,可以將字串讀出或寫入到檔案。 • fgets()函數中的字串名稱是字串讀取後的暫存區,「字串長度」是讀取的長度,單位是位元組,fgets()函數所讀入的「字串長度」有兩種情況,一種是讀取指定「字串長度」-1的字串,然後最後加上'\0'字元,一種是當「字串長度」-1的長度內包括了換行字元'\n'或EOF字元時,則只能讀取到這些字元為止。
fgets()函數讀取檔案的宣告示範與練習:CH09_4 • 程式範例是利用fgets()函數來讀取「巴冷公主.txt」檔案的資料。請注意在在第14行中,我們設定的讀取長度是31而不是30,這是因為fgets()只會讀取字串長度-1的字串,並於最後一個位置加上'\0'字元。
fputs()函數寫入檔案的宣告示範與練習:CH09_5 • 程式範例使用了fputs()函數將字串寫入檔案的方式,將「巴冷公主.txt」檔案的資料以字串方式讀取,再以字串方式寫入到「巴冷複製檔.txt」中。
格式化存取函數 • 除了單純以字元或字串方式寫入檔案外,也可以如同使用printf()與scanf()函數一樣,將要寫入的資料以特定格式寫入檔案中,這些格式存取函數是fprintf()與fscanf()兩種函數,首先來介紹寫入檔案的fprintf()函數。 • fprintf()函數除了多個檔案指標參數之外,其它部份如格式化控制字串,則與printf()函數完全相同,只不過printf()函數是將資料流輸出至螢幕,而fprintf()則是將資料流輸出至檔案。
fprintf()函數寫入檔案的示範與練習:CH09_6 • 程式範例將利用fprintf()函數,輸入兩筆學生資料,包括學號、姓名與成績,並以格式化字串方式寫入檔案。
fscanf()函數格式化讀取檔案的宣告示範與練習:CH09_7fscanf()函數格式化讀取檔案的宣告示範與練習:CH09_7 • fscanf()函數與scanf()函數相當類似,只是scanf()函數是由使用者的鍵盤輸入取得資料,而fscanf()函數則由檔案中讀取所指定的資料。
二進位檔操作簡介 • 二進位檔所儲存的資料有著存取速度快、佔用空間小以及可隨機存取資料的優點,雖然不能直接以一般文書編輯程式做檢視,不過在檔案管理上的確比文字檔案來的有效率許多。 • 二進位檔的存取模式,也需要開啟、關閉資料流,使用方法與文字檔存取方式相同。
二進位檔寫入函數 • 對於以二進位檔的方式寫入資料,可使用區段I/O函數中的fwrite()函數,並將所寫入的資料轉換為二進位碼。定義如下所示: • int fwrite(const void *ptr, int size, int count, FILE *檔案指標r);
fwrite()函數寫入檔案的宣告示範與練習:CH09_8 • 程式範例是將一個5個元素的整數陣列,以二進位方式寫入檔案「二進位檔.bin」中。執行完畢後,各位可用記事本開啟「二進位檔.bin」來觀察寫入的內容,將會看到一堆亂碼,這就是二進位檔的特性。
fwrite()函數寫入檔案的進階示範與練習:CH09_9 • 程式範例將試著以一個結構變數為單位,將學生通訊錄資料以二進位方式寫入檔案「record.bin」中。其中宣告結構型態List,並宣告成員變數w_name、w_tel、w_addr,並重新定義List為person型態。
二進位檔讀取函數 • 讀取二進位檔案的資料內容,就必須採取與寫入檔案時相同的資料型態,並使用fread()函數來讀取檔案,才可以正確讀出有意義的資訊。定義如下所示: • int fread(const void *ptr, int size, int count, FILE * 檔案指標);
freade()函數讀取檔案的示範與練習:CH09_10 • 程式範例將用來讀取與檢視剛才CH09_9範例所寫入的二進位檔案「record.bin」。
隨機存取檔案 • 隨機存取檔案最大的優點就是可隨機存取檔案中任何一筆資料,因為同樣類型的資料在寫入時的長度都是固定的,像是整數就一定是4位元組。 • 這樣的特性可以輕易計算出資料的所在位置,並透過移動檔案讀取游標來取得想要的資料。
檔案讀取游標 • 文字檔或二進位檔的檔案操作,都必須透過檔案讀取游標的移動。例如當各位使用fgetc()、fputc()、fgets()、fputs()、fprintf()、fscanf()等函數進行檔案資料的讀取時,檔案讀取游標都會自動往下一個位置移動。 • 檔案讀取游標就是一個指標,表示目前檔案讀取到哪一個位址。在Dec C++的命名為_ptr,當您宣告如下: • FILE *fptr;
檔案讀取游標的說明與示範:CH09_11 • 程式範例將利用gets()函數來分別讀取不同字串長度的資料,並顯示當時檔案讀取游標的位址,讓各位能夠更清楚它的作用。
隨機檔存取方式 • 所謂隨機檔存取方式就是程式可在檔案的任何地方配合檔案游標位置,作隨機式的存取資料。 • 在寫入二進位檔案時,很少以一筆筆不固定長度紀錄方寫入,通常會以一個結構為單位進行寫入。 • 這樣的好處是每筆資料長度都固定,因此可以輕易計算出讀取每筆資料時,檔案讀取游標所要移動的位移量。
fseek()函數移動與操作讀取游標示範:CH09_12 • 程式範例將根據3種mode常數,由使用者自行輸入位移量來讀取二進位檔案「record.bin」的每筆資料內容,各位可以分別輸入不同的位移量來觀察讀取資料間的差異。
fseek()函數隨機存取二進位檔的示範與練習:CH09_13fseek()函數隨機存取二進位檔的示範與練習:CH09_13 • 程式範例,除了可由使用者自行輸入筆數來讀取二進位檔案「record.bin」的該筆資料內容外,也能對該筆內容加以修改,示範了隨機存取模式的實作過程。
無緩衝區檔案存取操作 • 使用標準I/O檔案處理函數時,所有的存取動作都是針對緩衝區,直到關閉檔案時才將所有的資料寫入檔案中。 • 而使用低階I/O函數,您可以直接對磁碟進行存取,無需透過系統所設定的緩衝區。 • 優點是可以節省系統設定緩衝區的空間,但在存取時也無可避免會影響程式執行速度,因此有時程式設計師也會自行設定小型的緩衝區來解決。
基本檔案操作簡介 • 低階I/O檔案處理函數定義於io.h與fcntl.h中,其中要開啟與關閉檔案,則是使用open()與write()函數。首先介紹看open()函數的定義: • int open(char* 檔案名稱, int 開啟模式 , [存取屬性]);
檔案名稱 • 準備開啟的檔案名稱,必須包含完整檔案路徑,如果沒有指定路徑,則預設為目前的工作目錄。
存取屬性 • 通常open()函數中是不需要加入存取屬性,除非開啟模式中使用了O_CREAT常數,則必須選出該檔案的存取屬性。
低階I/O檔案處理函數 • 說明如下: • int write(int handle, char* buffer, int size); • int read(int handle, char* buffer, int size); • handle • 為檔案處理代碼。 • buffer • 為自行設定的緩衝區位址,一般緩衝區大小會設置為256的倍數。是讀取檔案或寫入檔案時,暫時存放資料的變數,一般為陣列變數。 • size • 為寫入或讀取資料的最大位元組數,因此buffer大小必須不小於size值。
無緩衝區檔案存取函數的示範與練習:CH09_14 • 程式範例使用無緩衝區檔案存取函數將字串寫入檔案中,然後再開啟相同檔案讀出所寫入的字串,並且設定了buffer緩衝區變數,以暫存寫入或讀出的字串。
無緩衝區檔案存取函數的示範與練習:CH09_15 • 程式範例將仍然是利用無緩衝區檔案存取函數將「巴冷公主.txt」檔完全複製到新建立的「巴冷複製檔.txt」檔。
無緩衝區隨機檔存取方式 • 無緩衝區隨機檔存取方式也可以在檔案的任何地方配合檔案游標位置,作隨機存取資料。 • lseek()函數的宣告與定義與fseek()函數類似,如下所示: • int fseek(int handle , long offset, int mode);
無緩衝區隨機檔存取方式的示範與練習:CH09_16無緩衝區隨機檔存取方式的示範與練習:CH09_16 • 程式範例將利用無緩衝區檔案存取函數lseek()來說明隨機檔讀取方式,各位可以自行比較與fseek()函數的差異之處。
檔案容量的計算練習:CH09_17 • fgetc()函式一次可以讀取一個字元,也就是一個位元組的大小,所以我們可以使用它來計算檔案的容量,只要每讀出一個位元組計數一次即可。