790 likes | 956 Views
第十一章. 檔案處理. 本章投影片僅供本書上課 教師 使用 , 非經同意請勿拷貝或轉載. 11-1 檔案 I/O 觀念. 一、檔案的種類 C 將檔案分為 1. 程式檔 (Program file) 2. 資料檔 (Data file) 為解決上述問題,將程式與資料分開存檔 需要資料時才打開資料檔,載入到變數或 陣列進行處理。 好處 資料易維護且同個程式可處理多個相同資料 格式的資料檔,增加程式的彈性。. P11-2. 二、資料檔的作業方式. 1. 建檔 (Create) 2. 新增 (Append) 3. 刪除 (Delete)
E N D
第十一章 檔案處理 本章投影片僅供本書上課教師使用,非經同意請勿拷貝或轉載
11-1 檔案I/O觀念 一、檔案的種類 • C 將檔案分為1. 程式檔(Program file) 2. 資料檔(Data file) • 為解決上述問題,將程式與資料分開存檔需要資料時才打開資料檔,載入到變數或陣列進行處理。 • 好處資料易維護且同個程式可處理多個相同資料格式的資料檔,增加程式的彈性。 P11-2
二、資料檔的作業方式 1. 建檔(Create) 2. 新增(Append) 3. 刪除(Delete) 4.修改(Update) 5. 查詢(Inquire) 6. 列印(Listing) 7. 分析(Analyze) P11-3
三、資料流觀念 • MS-DOS的檔案結構和UNIX一樣,整個檔案都是由一串連續的字元排列而成的稱為「資料流」(data stream)。 • 透過資料流觀念,可簡化高階輸出入動作,使得任何輸出入動作都在資料流上運作,如此在設計程式時,便可不必去理會週邊的多樣性。 P11-3
對任何週邊而言,進出的資料有兩種: 1.文字檔 表示每個字元包括數字都以該字元的ASCII碼來儲存 2.二進位檔 表示資料採用二進位格式存檔,如一般的 執行檔(.exe或.com)、聲音檔(.wav)、影像檔(.avi)、圖形檔(.pcx)等都是使用二進位格式 存檔。 P11-3
四、C語言的檔案處理函式 C 關於檔案處理方面,依照是否提供緩衝區分成兩種輸出入函式: 1. 標準輸出入函式 2. 系統輸出入函式 P11-4
一. 標準輸出入(Standard I/O)) • 亦稱 資料流輸出入(Stream I/O)函式,此類函式提供輸出入緩衝區來存取資料。只要在輸出入函式和週邊設備間建立管道後,便可讀寫資料檔。 • 緩衝區(Buffer)是指資料在做存取時,為增加程式執行的速度,在主記憶體中先建立一個緩衝區,透過此緩衝區來存取整塊資料,而不直接和磁碟產生關係。 P11-4
一. 標準輸出入(Standard I/O) 續 • 電腦發生當機或忘記關閉資料檔,會導致留在緩衝區內尚未回寫到磁碟的資料流失。 • 具有格式轉換能力,能將二進位格式存檔的數值資料自動轉換成以ASCII格式儲存的文字檔。 • 若使用緩衝區存取資料的輸出入函式,必須先宣告一個檔案指標,用來指向檔案的指標結構,透過此檔案指標來存取資料 • 由於其檔案(FILE)的原型,定義在stdio.h內。使用前要先含入”stdio.h”。 P11-4
二. 系統輸出入(System I/O)函式 • 是屬於低階服務,亦稱為「低階輸出入」函式。 • 不提供輸出入緩衝區,有資料需要做輸出入存取時,直接透過磁碟機馬上處理不提供緩衝區。 • 此種輸出入資料 Disk I/O頻繁影響程式執行速度。 • 系統突然當機,會影響到目前處理資料,比使用「資料流輸出入」函式影響整個緩衝區內資料損失還小。 • 此種函式不具格式化轉換能力,即無法在二進位格式和文字檔格式間自動做數值轉換。此種架構是使用檔案處理號碼(Handle No)來存取檔案。 P11-4
標準輸出入函式有四種不同讀寫(存取)資料的方法,系統輸出入函式只允許使用表中一種方法來存取資料:標準輸出入函式有四種不同讀寫(存取)資料的方法,系統輸出入函式只允許使用表中一種方法來存取資料: P11-5
11-2 資料流輸出入常用函式 • 所謂「檔案指標」(file pointer)是指向下次即將要存取指定資料檔中某個Byte的位置。 • 將指定的資料寫入資料檔方式和讀取方式差不多,只是方向相反,先使用fopen()函式找出指定寫入的資料檔,若檔案有找到將它開啟,系統在主記憶體中都會保留一塊輸出緩衝區,並設定一個檔案指標指到該緩衝區的開頭。 • 透過fprintf()函式(循序檔)或fwrite()函式(隨機檔),將程式中指定的資料寫到目前檔案指標指到的緩衝區位置。 P11-5
寫完一筆資料,檔案指標會自動往下移,繼續寫入資料一直到緩衝區的資料填滿時,系統自動將放在輸出緩衝區內的資料全部寫入指定磁碟機中的資料檔,並將緩衝區內的資料清除以及將檔案指標移到緩衝區的開頭,繼續等待寫入資料。寫完一筆資料,檔案指標會自動往下移,繼續寫入資料一直到緩衝區的資料填滿時,系統自動將放在輸出緩衝區內的資料全部寫入指定磁碟機中的資料檔,並將緩衝區內的資料清除以及將檔案指標移到緩衝區的開頭,繼續等待寫入資料。 • 當結束寫入的動作要記得使用fclose()函式來關閉檔案。 • 由於是寫入模式,fclose()函式會先將緩衝區內尚未寫入的資料寫回磁碟指定資料檔後,再將此緩衝區由主記憶體中釋放歸還給系統。 P11-7
資料流輸出入存取資料時常用的函式: P11-7
一、開啟檔案 • 欲存取資料檔的第一個步驟就是先透過fopen()函式將該資料檔打開,設定存取模式以及指定實際的資料檔名。 • 應注意資料檔是以那種方式存檔,讀取資料時也必須使用同一模式來讀取資料。 • fopen的語法如下: 語法:FILE *fopen (const char *filename, const char *mode); 功能:以指定模式開啟檔案,若開檔成功會傳回一 個指向FILE結構的指標。若開檔失敗,會傳 回 NULL。 P11-8
P11-9 3.檔案指標宣告 #include<stdio.h> FILE *fptr; fptr=fopen(“d:\\ch11\\c11_2_1\\student.dat”,”w”); fptr=fopen(“d:\\ch11\\c11_2_1\\student.dat”,”w”)); if (fptr==NULL) { printf(“ Open-file is fail !”); exit(0); } 上面第1-2行敘述可合併成一行敘述: if ((fptr=fopen(“d:\\ch11\\c11_2_1\\student.dat”,”w”))==NULL) { printf(“ Open-file is fail !”); exit(0);}
4. char *mode(模式): P11-10
二、關閉檔案 • 程式執行時,若資料檔不再使用時應記得使用fclose()函式將該檔案關閉。 • 若檔案是讀取模式,沒關檔不會發生問題。 • 資料檔若是寫入模式,fclose()函式會先將輸出緩衝區內未回寫資料,先寫回指定磁碟機指定資料夾下的資料檔,同時在資料檔的尾端加上一個檔案結束符號(End of file:EOF),表示資料到此結束。 • 語法: int fclose(FILE *stream); P11-11
三、feof函式 • 讀取資料檔時,可藉此函式來檢查檔案指標是否已指到EOF符號 如檔案指標指到EOF符號,表示資料已 讀完,此函式會傳回不為零的整數。 如未指到EOF符號,傳回0。 • 語法: 語法:int feof(FILE *stream) 功能:用來測試檔案是否結束。 P11-12
四、fileno函式 • 開啟一個檔案成功時,系統自動賦予一個檔案代碼(handle_no)用來代表該檔案。 • 檔案代碼有如醫院的掛號,一直領到藥之前都以此號碼代表,是以邏輯名稱代替真實名稱。 • 程式中打開多個資料檔時,可透過 fileno(fptr)函式來取得其檔案代碼,fptr 即是使用FILE宣告的檔案指標。 • 語法 語法:int fileno(FILE *stream) 功能:將指定資料檔的代碼(handle_no)傳回。 P11-13
五、filelength函式 • 資料檔大小可透過此函式來取得。 • 隨機檔每筆資料長度是固定,可透過sizeof()函式求一筆資料的長度,兩者相除,可求出該資料檔共有多少筆資料。 • 語法: 語法:long filelength(int handle_no) 功能:將檔案代碼所指到資料檔的長度(Bytes)以長整數方式傳回。 P11-13
11-3 循序存取 • C 對資料存取方式 循序存取檔 隨機存取檔 • 循序存取檔簡稱循序檔,資料寫入磁碟機的方式是後輸入的資料放在剛輸入資料的後面,按照資料先後次序一個接一個放。 • 讀取資料亦由第一筆資料開始讀取。 • 新增的資料放在舊資料的最後面。 • 每筆資料長度可不一樣,較省磁碟空間 • 資料查詢時,每筆資料長度不一,每次查詢都必須從頭開始找起,愈後面的資料找尋的時間愈長。 P11-14
一、循序檔的特性 1. 每筆記錄長度可不一樣。 2. 增加資料時,要從原有資料的最後一筆開始。 3. 每次找尋資料時都要從頭開始找起,費時 且效率低。 4. 每筆資料長度差異很大時,使用循序檔來存資料可節省記憶體空間。 P11-14
二、循序檔的使用時機 1. 每筆記錄長度無法確定時。 2. 資料不經常做新增、修改和刪除作業時。 3. 處理資料時是依序連續處理,而非跳來跳去。 P11-15
三、字元資料檔的讀寫 • getche() 函式和 getc() 函式都用來讀取字元的 • getche()函式是由鍵盤讀取字元 • getc()函式是由檔案指標所指的資料檔中讀取字元。 • while((ch=getc(fptr))!=EOF) printf("%c",ch); fclose(fptr); P11-16
// FileName : rdwtchar.c 01 #include <stdio.h> 02 #include <stdlib.h> 04 int main(int argc, char *argv[]) 05 { 06 FILE *fptr; 07 char ch; 10 if((fptr=fopen("d:\\myfile1.txt","w"))==NULL) 11 { 12 printf(" Sorry! 檔案無法開啟 ! ..."); 13 exit(0); 14 } 15 while(1) 16 { 17 printf("\n\n 請輸入一個字元 : ( 按 '/' - 結束 : "); 18 ch=getche(); 19 if(ch=='/') 20 { 21 fclose(fptr); 22 break; 23 } 24 putc(ch,fptr); 25 }
// FileName : rdwtchar.c 01 #include <stdio.h> 02 #include <stdlib.h> 04 int main(int argc, char *argv[]) 05 { 06 FILE *fptr; 07 char ch; 10 if((fptr=fopen("d:\\myfile1.txt","w"))==NULL) 11 { 12 printf(" Sorry! 檔案無法開啟 ! ..."); 13 exit(0); 14 } 15 while(1) 16 { 17 printf("\n\n 請輸入一個字元 : ( 按 '/' - 結束 : "); 18 ch=getche(); 19 if(ch=='/') 20 { 21 fclose(fptr); 22 break; 23 } 24 putc(ch,fptr); 25 }
四、字串資料檔的讀寫 • gets()函式會等待由鍵盤輸入的字串待按<Enter>鍵時才結束輸入串存入字元陣列,但 <Enter> 鍵不存入。 • fputs()函式可將連續輸入多行字串存入檔案。 • fgets()函式由檔案讀出字串,由於每行的<Enter>鍵未存入檔案內,導致讀出資料無法和寫入時能分行顯示。 • 每行字串存入資料檔時,先使用fputs(str,fptr)將字串置入目前檔案指標的位置fputs(“\n”,fptr) 在字串尾端插入換行符號(\n)才能達成。寫法: fputs(str,fptr); fputs("\n",fptr); P11-19
// FileName:rdwtstr.c 04 int main(int argc, char *argv[]) 05 { 06 FILE *fptr; 07 char str[81]; 08 if((fptr=fopen("d:\\myfile2.txt","w"))==NULL) 09 { 10 printf(" Sorry ! 檔案無法開啟 ...."); 11 exit(0); 12 } 13 printf("請輸入字串 (連按Enter鍵兩次結束) : \n"); 14 while(1) 15 { 16 gets(str); 17 if (strlen(str)>0) 18 { 19 fputs(str,fptr); 20 fputs("\n",fptr); 21 } 22 else 23 { 24 fclose(fptr); 25 break; 26 } 27 }
29 //由myfile2.txt讀取字串 30 if((fptr=fopen("d:\\myfile2.txt","r"))==NULL) 31 { 32 printf("Sorry ! 檔案無法開啟 .... \n\n"); 33 system("PAUSE"); 34 exit(0); 35 } 36 printf("\n==== myfile2.txt 資料檔的內容如下 : \n"); 37 while((fgets(str,80,fptr))!=NULL) 38 { 39 printf("%s",str); 40 } 41 fclose(fptr); 42 printf("\n\n"); 43 system("PAUSE"); 44 return 0; 45 }
五、格式化文字資料檔的讀寫 語法: int fprintf(檔案指標,”格式化字串”[,參數串列]); 功能:以格式化將資料寫入指定路徑的資料檔中。 P11-22
【說明】 1. 檔案指標是以FILE *fptr 所宣告的檔案指標。如: P11-22
// Filename : s_create.c 03 FILE *outf; 04 char conti='y'; 06 int main(int argc, char *argv[]) 07 { 08 char pname[8]; 09 int chi,eng,mat,no=0; 11 outf=fopen("d:\\student.dat","w"); 12 while(toupper(conti)=='Y') 13 { 14 no++; 15 printf("\n\n=== 現在 建立第 %d 位學生的成績 ===", no); 16 printf("\n\n 1. 姓名 : "); 17 scanf("%s",&pname); 18 printf(" 2. 國文 : "); 19 scanf("%d",&chi); 20 printf(" 3. 英文 : "); 21 scanf("%d",&eng); 22 printf(" 4. 數學 : "); 23 scanf("%d",&mat); 24 fprintf(outf," %s %d %d %d",pname,chi,eng,mat); 25 printf("\n 是否繼續? (Y/N) : "); 26 conti=getche(); 27 } 28 fclose(outf); 29 printf("\n\n"); 30 system("PAUSE"); 31 return 0; 32 }
(二) fscanf讀取函式 語法:int fscanf(檔案指標,”格式化字串” [,變數位址串列]); 功能:由檔案指標所指的資料檔中讀取資料,將讀出的資料以指定格式存入指定的變數中。 P11-25
// Filename: s_list.c 03 FILE *inf; 04 int main(int argc, char *argv[]) 05 { 06 char grade,pname[8]; 07 int i,chi,eng,mat; 08 float avg; 10 inf=fopen("d:\\student.dat","r"); 11 printf("\n 姓名 國文 英文 數學 平均 等第 "); 12 printf("\n ======== ======== ======= ======= ======= ======= "); 13 while(!feof(inf)) 14 { 15 fscanf(inf,"%s %d %d %d",&pname,&chi,&eng,&mat); 16 avg=(chi+eng+mat)/3.0; 17 grade=avg<60 ? 'D': (avg<70 ? 'C' : (avg<80 ? 'B':'A')); 18 printf("\n %s %d %d %d %4.1f %c ", 19 pname,chi,eng,mat,avg,grade); 20 } 21 printf("\n ==============================================\n\n"); 22 fclose(inf); 23 system("PAUSE"); 24 return 0; 25 }
六、結構化I/O存取函式 • 結構I/O(或稱記錄I/O),存取資料檔都以整塊資料為存取單位。 • 適用於陣列、結構或是結構陣列所建立資料檔。 • 檔案模式不能設為本文檔格式(w、r、a),必須設為二進位檔格式(wb、rb、ab)。 • 結構I/O使用fread()函式由資料檔讀取整塊資料。 fwrite()函式將整塊資料寫入資料檔。 fseek()函式來移動檔案指標。 P11-28
一、fwrite() 語法:size_t fwrite(const void *ptr,size_t size,size_t n,FILE *stream); 功能:將資料寫入隨機檔中。 二、fread() 語法:size_t fread(void *ptr,size_t size,size_t n,FILE *stream); 功能:由隨機檔中讀取資料。 三、fseek() 語法:int fseek (FILE *stream, long offset, int whence); 功能:將檔案指標由whence指定處移動offsetbytes。 P11-28
fseek() : 1. stream:為檔案指標名稱必須和fopen函式的檔案指標名稱相同。 2. offset:為指標位移,資料型別必須為long,若offset為正值表示將檔案指標由設定點往後移動offset Bytes。若為負值,表示將檔案指標由設定點往前移動offset Bytes。 3. whence:開始尋找資料位置: P11-30
七、結構化二進位資料檔的存取 P11-30
// FileName : r_creat.c 01 #include <stdio.h> 02 #include <stdlib.h> 03 #include<ctype.h> 04 struct bank_struct 05 { 06 char flag; 07 long account_no; 08 char name[8]; 09 long saving; 10 }; 11 struct bank_struct person; 12 FILE *fptr; 13 14 int main(int argc, char *argv[]) 15 { 16 int k=1; 17 char *file_name="d:\\bank.dat"; 18
19 if((fptr=fopen(file_name,"wb"))==NULL) 20 { 21 printf(" Sorry ! 開檔失敗 .... "); 22 getche(); 23 exit(1); 24 } 25 do 26 { 27 printf("\n\n=== 存提款作業 ==="); 28 printf("\n\n 現在 新建第 %d 位客戶的資料 " , k++); 29 printf("\n\n 1. 帳 號 : "); 30 scanf("%D",&person.account_no); 31 printf("\n 2. 姓 名 : "); 32 scanf("%s",&person.name); 33 printf("\n 3. 存入金額 : "); 34 scanf("%D",&person.saving); 35 fwrite(&person,sizeof(person),1,fptr); 36 printf("\n 是否繼續 ? (Y/N) : "); 37 }while (toupper(getche())=='Y'); 38 fclose(fptr); 39 printf("\n\n"); 40 system("PAUSE"); 41 return 0; 42 }
3. 列印作業 用來一次顯示指定資料檔內所有資料。將上例所建資料檔,由第一筆記錄開始依序顯示至最後一筆為止。 P11-34