第 9 章 檔案入門與處理

48
1 第9第 第第第第第第第 9-1 檔檔檔檔 9-2 檔檔檔檔檔檔檔 9-3 檔檔檔檔檔檔檔檔 9-4 檔檔檔檔檔檔 9-5 檔檔檔檔檔檔檔檔檔檔 9-6 檔檔檔檔檔檔

Upload: ulmer

Post on 19-Jan-2016

76 views

Category:

Documents


0 download

DESCRIPTION

第 9 章 檔案入門與處理. 9-1 檔案簡介 9-2 文字檔操作簡介 9-3 二進位檔操作簡介 9-4 隨機存取檔案 9-5 無緩衝區檔案存取操作 9-6 本章綜合練習. 9-1 檔案簡介. 每個檔案都必須有一個代表它的檔案名稱 (File Name) ,檔名可區分成「主檔名」與「副檔名」,中間以句點 (.) 做分隔,透過這樣的命名方式可以清楚分辨其名稱與類型。如下所示: 主檔名 . 副檔名. 認識資料流. - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: 第 9 章  檔案入門與處理

1

第 9章 檔案入門與處理

9-1 檔案簡介9-2 文字檔操作簡介9-3 二進位檔操作簡介 9-4 隨機存取檔案9-5 無緩衝區檔案存取操作9-6 本章綜合練習

Page 2: 第 9 章  檔案入門與處理

2

9-1 檔案簡介

每個檔案都必須有一個代表它的檔案名稱(File Name) ,檔名可區分成「主檔名」與「副檔名」,中間以句點 (.) 做分隔,透過這樣的命名方式可以清楚分辨其名稱與類型。如下所示: 主檔名 . 副檔名

Page 3: 第 9 章  檔案入門與處理

3

認識資料流

資料流 (stream) 的主要功用是作為程式與周邊的資料傳輸管道,而在 C 語言中,檔案的處理正是透過資料流 (stream) 方式存取資料。如下所示:

Page 4: 第 9 章  檔案入門與處理

4

緩衝區

「緩衝區」 (buffer) ,就是在程式執行時,所提供的額外記憶體,可用來暫時存放準備執行的資料。

緩衝區的設置是為了存取效率上的考量,因為記憶體的存取速度會比磁碟機來得快速。

Page 5: 第 9 章  檔案入門與處理

5

設定緩衝區

Page 6: 第 9 章  檔案入門與處理

6

未設定緩衝區

當使用標準 I/O 函數時,系統會自動設定緩衝區,並透過資料流來讀寫檔案。

所有存取動作都是針對緩衝區,直到關閉檔案才將所有資料寫入磁碟檔案。

Page 7: 第 9 章  檔案入門與處理

7

檔案的種類

文字檔 文字檔是以字元編碼的方式進行儲存,在 Windo

ws 作業系統的記事本( NotePad )程式中則預設以 ASCII 編碼來儲存文字檔,每個字元佔有 1位元組。

二進位檔 所謂二進位檔,就是以二進位格式儲存,將記憶

體中資料原封不動儲存至檔案之中,適用於非字元為主的資料。

Page 8: 第 9 章  檔案入門與處理

8

檔案存取方式

循序式存取( sequential access ) 也就是由上往下,一筆一筆讀取檔案的內容。如

果要儲存資料時,則將資料附加在檔案的尾端,這種存取方式常用於文字檔案,而被存取的檔案則稱為循序檔。

隨機式存取( random access ) 「隨機存取檔」多半是以二進位檔案為主,會以

一個完整單位來進行資料的寫入,這筆單位通常以結構為單位,例如結構中可能包括了一個帳戶的名稱、餘額、投資款項等。

Page 9: 第 9 章  檔案入門與處理

9

文字檔操作簡介

C 對文字檔的處理方式主要是透過標準 I/O函數來進行檔案的開啟、寫入、關閉與設定緩衝區,相關存取函數有 fopen() 、 fclose() 、 fgets() 、 fputs() 、 fprintf() 、 fscanf() 等,都定義在 stdio.h 標頭檔案中。

Page 10: 第 9 章  檔案入門與處理

10

檔案的開啟與關閉

要進行檔案存取,首先必須開啟資料流,也就是進行開啟檔案的動作。在 C 中,檔案開啟時必須先透過 <stdio.h> 中定義的 FILE 型態建立一個指標變數: FILE* pf; /* 建立用來指向檔案的指標 */

當指標變數建立完成之後,才可以透過檔案開啟函數 fopen() 開啟檔案。 fopen() 函數定義如下: FILE* fopen(char* 檔案名稱 , char* 開啟模式

字串 ); /* 傳回 FILE 指標 */

Page 11: 第 9 章  檔案入門與處理

11

檔案名稱

包括檔案路徑,如果沒有指定路徑,則會預設為目前的工作目錄。如果要直接指定檔案路徑,則因為反斜線為 C 中的控制字元,所以必須多加上一個反斜線。

例如以下想開啟一個在 C:\temp 資料夾下的 test.txt 檔案: pf = fopen("C:\\temp\\test.txt", "r"); /* 多加上一

個反斜線 */

Page 12: 第 9 章  檔案入門與處理

12

開啟模式字串表

開啟模式字串 說明

r 讀取檔案,不寫入任何內容,此為唯讀模式。

w 寫入檔案並覆蓋檔案,若檔案不存在會產生新檔案。

a 新增資料於原檔案尾端,若檔案不存在會產生新檔案。

r+ 讀取或修改檔案。

w+ 讀取或寫入檔案並覆蓋檔案,若檔案不存在會產生新檔案。

a+新增資料於原檔案尾端,若檔案不存在會產生新檔案,作用與 a相同。

Page 13: 第 9 章  檔案入門與處理

13

fopen() 函數與 fclose() 函數的宣告示範與練習: CH09_1

在 Windows 系統的記事本程式中建立一個文字檔 "test.txt" ,再利用以下程式範例來練習 fopen() 函數與 fclose() 函數的宣告用法。

Page 14: 第 9 章  檔案入門與處理

14

字元存取函數

利用 fgetc() 函數可從檔案資料流中一次讀取一個字元,然後將讀取游標往下一個字元移動,並且逐步將檔案的內容讀出。 fgetc() 函數的定義如下: int fgetc(FILE * 檔案指標 );

如果字元讀取成功,則傳回所讀取字元,否則就傳回 EOF ( End of File )。另外要判斷檔案是否讀取完畢,可以利用 feof() 函數來進行檢查。

Page 15: 第 9 章  檔案入門與處理

15

fgetc() 函數與 feof() 函數的宣告示範與練習: CH09_2

程式範例是簡單開啟一個檔案,並利用 fgetc() 函數與 while迴圈來讀出此檔案中的所有字元資料內容。

Page 16: 第 9 章  檔案入門與處理

16

fputc() 函數寫入檔案的宣告示範與練習:CH09_3

程式範例可以讓使用者指定檔案名稱,並利用 fputc() 函數將使用者鍵盤所輸入內容寫入檔案中,直到按下 Enter鍵為止。

Page 17: 第 9 章  檔案入門與處理

17

字串存取函數

在標準 I/O 函數中的字串存取函數有 fgets() 函數與 fputs() 函數兩種,可以將字串讀出或寫入到檔案。

fgets() 函數中的字串名稱是字串讀取後的暫存區,「字串長度」是讀取的長度,單位是位元組, fgets() 函數所讀入的「字串長度」有兩種情況,一種是讀取指定「字串長度」 -1 的字串,然後最後加上 '\0' 字元,一種是當「字串長度」 -1 的長度內包括了換行字元 '\n'或 EOF 字元時,則只能讀取到這些字元為止。

Page 18: 第 9 章  檔案入門與處理

18

fgets() 函數讀取檔案的宣告示範與練習:CH09_4

程式範例是利用 fgets() 函數來讀取「巴冷公主 .txt 」檔案的資料。請注意在在第 14 行中,我們設定的讀取長度是 31 而不是 30 ,這是因為 fgets()只會讀取字串長度 -1 的字串,並於最後一個位置加上 '\0' 字元。

Page 19: 第 9 章  檔案入門與處理

19

fputs() 函數寫入檔案的宣告示範與練習:CH09_5

程式範例使用了 fputs() 函數將字串寫入檔案的方式,將「巴冷公主 .txt 」檔案的資料以字串方式讀取,再以字串方式寫入到「巴冷複製檔 .txt 」中。

Page 20: 第 9 章  檔案入門與處理

20

格式化存取函數

除了單純以字元或字串方式寫入檔案外,也可以如同使用 printf() 與 scanf() 函數一樣,將要寫入的資料以特定格式寫入檔案中,這些格式存取函數是 fprintf() 與 fscanf()兩種函數,首先來介紹寫入檔案的 fprintf() 函數。

fprintf() 函數除了多個檔案指標參數之外,其它部份如格式化控制字串,則與 printf() 函數完全相同,只不過 printf() 函數是將資料流輸出至螢幕,而 fprintf() 則是將資料流輸出至檔案。

Page 21: 第 9 章  檔案入門與處理

21

fprintf() 函數寫入檔案的示範與練習: CH09_6

程式範例將利用 fprintf() 函數,輸入兩筆學生資料,包括學號、姓名與成績,並以格式化字串方式寫入檔案。

Page 22: 第 9 章  檔案入門與處理

22

fscanf() 函數格式化讀取檔案的宣告示範與練習: CH09_7

fscanf() 函數與 scanf() 函數相當類似,只是 scanf() 函數是由使用者的鍵盤輸入取得資料,而 fscanf() 函數則由檔案中讀取所指定的資料。

Page 23: 第 9 章  檔案入門與處理

23

二進位檔操作簡介

二進位檔所儲存的資料有著存取速度快、佔用空間小以及可隨機存取資料的優點,雖然不能直接以一般文書編輯程式做檢視,不過在檔案管理上的確比文字檔案來的有效率許多。

二進位檔的存取模式,也需要開啟、關閉資料流,使用方法與文字檔存取方式相同。

Page 24: 第 9 章  檔案入門與處理

24

二進位模式表 存取模式 說明rb 開啟二進位檔案進行讀取動作,不寫入任何內容

wb 產生一個新的二進位檔案,如果有同名檔案存在,則該原檔案會被丟棄,另開啟新檔案

ab 開啟一個已存在的二進位檔案,所寫入的檔案會附加在原檔案的尾端;如果指定的檔案不存在,則產生一個新的檔案

r+b 開啟一個已經存在的二進位檔案,以進行讀取或修改,也可以寫作 rb+。

w+b 產生一個新的二進位檔案,以進行讀取或寫入,如果有同名檔案存在,則該原檔案會被丟棄,也可以寫作 wb+

a+b 開啟一個已存在的二進位檔案,所寫入的檔案會附加在原檔案的尾端;如果指定的檔案不存在,則產生一個新的檔案,作用與 ab相同,也可以寫作 ab+

Page 25: 第 9 章  檔案入門與處理

25

二進位檔寫入函數

對於以二進位檔的方式寫入資料,可使用區段 I/O 函數中的 fwrite() 函數,並將所寫入的資料轉換為二進位碼。定義如下所示: int fwrite(const void *ptr, int size, int count, FIL

E * 檔案指標 r);

Page 26: 第 9 章  檔案入門與處理

26

fwrite() 函數寫入檔案的宣告示範與練習:CH09_8

程式範例是將一個 5 個元素的整數陣列,以二進位方式寫入檔案「二進位檔 .bin 」中。執行完畢後,各位可用記事本開啟「二進位檔 .bin 」來觀察寫入的內容,將會看到一堆亂碼,這就是二進位檔的特性。

Page 27: 第 9 章  檔案入門與處理

27

fwrite() 函數寫入檔案的進階示範與練習:CH09_9

程式範例將試著以一個結構變數為單位,將學生通訊錄資料以二進位方式寫入檔案「 record.bin 」中。其中宣告結構型態 List ,並宣告成員變數 w_name 、 w_tel 、 w_addr ,並重新定義 List 為 person 型態。

Page 28: 第 9 章  檔案入門與處理

28

二進位檔讀取函數

讀取二進位檔案的資料內容,就必須採取與寫入檔案時相同的資料型態,並使用 fread() 函數來讀取檔案,才可以正確讀出有意義的資訊。定義如下所示: int fread(const void *ptr, int size, int count, FILE

* 檔案指標 );

Page 29: 第 9 章  檔案入門與處理

29

freade() 函數讀取檔案的示範與練習: CH09_10

程式範例將用來讀取與檢視剛才 CH09_9 範例所寫入的二進位檔案「 record.bin 」。

Page 30: 第 9 章  檔案入門與處理

30

隨機存取檔案

隨機存取檔案最大的優點就是可隨機存取檔案中任何一筆資料,因為同樣類型的資料在寫入時的長度都是固定的,像是整數就一定是 4 位元組。

這樣的特性可以輕易計算出資料的所在位置,並透過移動檔案讀取游標來取得想要的資料。

Page 31: 第 9 章  檔案入門與處理

31

檔案讀取游標

文字檔或二進位檔的檔案操作,都必須透過檔案讀取游標的移動。例如當各位使用 fgetc() 、 fputc() 、 fgets() 、 fputs() 、 fprintf() 、 fscanf() 等函數進行檔案資料的讀取時,檔案讀取游標都會自動往下一個位置移動。

檔案讀取游標就是一個指標,表示目前檔案讀取到哪一個位址。在 Dec C++ 的命名為 _ptr ,當您宣告如下: FILE *fptr;

Page 32: 第 9 章  檔案入門與處理

32

檔案讀取游標的說明與示範: CH09_11

程式範例將利用 gets() 函數來分別讀取不同字串長度的資料,並顯示當時檔案讀取游標的位址,讓各位能夠更清楚它的作用。

Page 33: 第 9 章  檔案入門與處理

33

隨機檔存取方式

所謂隨機檔存取方式就是程式可在檔案的任何地方配合檔案游標位置,作隨機式的存取資料。

在寫入二進位檔案時,很少以一筆筆不固定長度紀錄方寫入,通常會以一個結構為單位進行寫入。

這樣的好處是每筆資料長度都固定,因此可以輕易計算出讀取每筆資料時,檔案讀取游標所要移動的位移量。

Page 34: 第 9 章  檔案入門與處理

34

mode 常數表

mode常數 說明SEEK_SET 游標起始點位於檔案的起始位置,往後移

動 offset個位元組。SEEK_CUR 游標起始點為目前檔案游標位置,往後移

動 offset個位元組。SEEK_END 游標起始點位於檔案的結尾,往前移動

offset個位元組,此時 offset為負值。

Page 35: 第 9 章  檔案入門與處理

35

fseek() 函數移動與操作讀取游標示範: CH09_12

程式範例將根據 3 種 mode 常數,由使用者自行輸入位移量來讀取二進位檔案「 record.bin 」的每筆資料內容,各位可以分別輸入不同的位移量來觀察讀取資料間的差異。

Page 36: 第 9 章  檔案入門與處理

36

fseek() 函數隨機存取二進位檔的示範與練習: CH09_13

程式範例,除了可由使用者自行輸入筆數來讀取二進位檔案「 record.bin 」的該筆資料內容外,也能對該筆內容加以修改,示範了隨機存取模式的實作過程。

Page 37: 第 9 章  檔案入門與處理

37

無緩衝區檔案存取操作

使用標準 I/O 檔案處理函數時,所有的存取動作都是針對緩衝區,直到關閉檔案時才將所有的資料寫入檔案中。

而使用低階 I/O 函數,您可以直接對磁碟進行存取,無需透過系統所設定的緩衝區。

優點是可以節省系統設定緩衝區的空間,但在存取時也無可避免會影響程式執行速度,因此有時程式設計師也會自行設定小型的緩衝區來解決。

Page 38: 第 9 章  檔案入門與處理

38

基本檔案操作簡介

低階 I/O 檔案處理函數定義於 io.h 與 fcntl.h中,其中要開啟與關閉檔案,則是使用 open() 與 write() 函數。首先介紹看 open() 函數的定義: int open(char* 檔案名稱 , int 開啟模式 , [ 存取屬性 ]);

Page 39: 第 9 章  檔案入門與處理

39

檔案名稱

準備開啟的檔案名稱,必須包含完整檔案路徑,如果沒有指定路徑,則預設為目前的工作目錄。

Page 40: 第 9 章  檔案入門與處理

40

開啟模式

開啟模式常數 說 明

O_RDONLY 可供讀取檔案,此為唯讀模式。O_WRONLY 可供寫入檔案,不能讀取檔案。O_RDWR 開啟可讀取與可寫入的檔案O_CREAT 若檔案不存在,則建立新的檔案,若檔案存在,

則此指令無效。O_APPEND 以附加的方式寫入檔案,附加在原檔案後方。O_TEXT 開啟文字檔案。O_BINARY 開啟二進位檔案。

Page 41: 第 9 章  檔案入門與處理

41

存取屬性

通常 open() 函數中是不需要加入存取屬性,除非開啟模式中使用了 O_CREAT 常數,則必須選出該檔案的存取屬性。

存取屬性常數 說 明S_IWRITE 新建立的檔案,只可供寫入。S_WRONLY 新建立的檔案,只可供讀取。S_IWRITE |

S_WRONLY新建立的檔案,可讀取與可寫入的檔案。

Page 42: 第 9 章  檔案入門與處理

42

低階 I/O 檔案處理函數 說明如下:

int write(int handle, char* buffer, int size); int read(int handle, char* buffer, int size);

handle 為檔案處理代碼。

buffer 為自行設定的緩衝區位址,一般緩衝區大小會設置為 2

56 的倍數。是讀取檔案或寫入檔案時,暫時存放資料的變數,一般為陣列變數。

size 為寫入或讀取資料的最大位元組數,因此 buffer大小

必須不小於 size值。

Page 43: 第 9 章  檔案入門與處理

43

無緩衝區檔案存取函數的示範與練習: CH09_14

程式範例使用無緩衝區檔案存取函數將字串寫入檔案中,然後再開啟相同檔案讀出所寫入的字串,並且設定了 buffer 緩衝區變數,以暫存寫入或讀出的字串。

Page 44: 第 9 章  檔案入門與處理

44

無緩衝區檔案存取函數的示範與練習: CH09_15

程式範例將仍然是利用無緩衝區檔案存取函數將「巴冷公主 .txt 」檔完全複製到新建立的「巴冷複製檔 .txt 」檔。

Page 45: 第 9 章  檔案入門與處理

45

無緩衝區隨機檔存取方式

無緩衝區隨機檔存取方式也可以在檔案的任何地方配合檔案游標位置,作隨機存取資料。

lseek() 函數的宣告與定義與 fseek() 函數類似,如下所示: int fseek(int handle , long offset, int mode);

Page 46: 第 9 章  檔案入門與處理

46

mode 常數表

mode常數 說明SEEK_SET 游標起始點位於檔案的起始位置,往後移

動 offset個位元組。SEEK_CUR 游標起始點為目前檔案游標位置,往後移

動 offset個位元組。SEEK_END 游標起始點位於檔案的結尾,往前移動

offset個位元組,此時 offset為負值。

Page 47: 第 9 章  檔案入門與處理

47

無緩衝區隨機檔存取方式的示範與練習:CH09_16

程式範例將利用無緩衝區檔案存取函數 lseek()來說明隨機檔讀取方式,各位可以自行比較與 fseek() 函數的差異之處。

Page 48: 第 9 章  檔案入門與處理

48

檔案容量的計算練習: CH09_17

fgetc() 函式一次可以讀取一個字元,也就是一個位元組的大小,所以我們可以使用它來計算檔案的容量,只要每讀出一個位元組計數一次即可。