Speaker: Wen-Ching Lo Date: 2009/12/23 12/23計程實習課 Speaker: Wen-Ching Lo Date: 2009/12/23
Outline 檔案的輸入與輸出 Structure
檔案的輸入與輸出 C軟體提供了許多檔案的輸入與輸出函數,這些函數可分成兩大類。 有緩衝區的輸入與輸出(Buffered I/O) 無緩衝區的輸入與輸出(Unbuffered file I/O) 有緩衝區的輸入與輸出,是當它在讀取檔案資料或將資料寫入檔案時,一定都先經過一個緩衝區。 沒有緩衝區的輸入與輸出,表示輸入與輸出的動作,是直接在磁碟內執行讀取資料和寫入資料的動作。 由於有緩衝區的輸入與輸出函數是包含在stdio.h檔 案內,所以程式前面,必須要包含下列指令。 # include <stdio.h >
函數名稱 功 能 說 明 fopen( ) 開啟一個檔案 fclose( ) 關閉一個檔案 putc( ) 輸出一個字元到檔案 getc( ) 從某一個檔案讀取一個字元 fprintf( ) 輸出資料至某檔案 fscanf( ) 從某檔案讀取資料 feof( ) 測試是否到了檔案結束位置 ferror( ) 測試檔案操作是否正常 fseek( ) 設定準備讀取檔案資料的位置 rewind( ) 將準備讀取檔案資料置,設定在檔案起始位置 remove( ) 檔案的刪除
fopen( ) FILE *fopen(char *filename, char *mode); “r”:開啟一個文字檔(text),供程式讀取。 “w”:開啟一個文字檔,供程式將資料寫入此檔案內。如果磁碟內不包含這個檔案,則系統會自行建立這個檔案。如果磁碟內包含這個檔案,則此檔案內容會被蓋過而消失。 “a”:開啟一個文字檔,供程式將資料寫入此檔案的末端。如果此檔案不存在,則系統會自行建立此檔案。
fopen( ) FILE *fopen(“filename”, “mode”); “rb”:開啟一個二元檔(binary),供程式讀取。 “wb”:開啟一個二元檔,供程式資料寫入此檔案內。如果磁碟內不包含這個檔案,則系統會自行建立這個檔案。如果磁碟內包含這個檔案,則此檔案內容會被蓋過而消失。 “ab”:開啟一個二元檔,供程式將資料寫入此檔案末端,如果此檔案不存在,則系統會自行建立此檔案。 前述的fopen( )函數的使用格式是以指標方式設計,而經常 也會直接以字串方式設計此函數: FILE *fopen(“filename”, “mode”);
fclose( ) fclose( )為關閉檔案的函數。C語言中關閉檔案主要有兩個目的: 促使檔案在關閉前會將檔案緩衝區資料寫入磁碟檔案內,否則檔案緩衝區資料會遺失。 一個C語言程式,在同一時間可開啟的檔案數量有限,一般是20個,如果你的程式很大,要開啟超過20個檔案時,你必須將暫時不用的檔案關閉。 fclose( )執行失敗,它的傳回值是非零值,如果 fclose()執行成功,它的傳回值是零。 使用格式: int fclose(FILE *fname);
fputc( ) putc( )函數的主要功能是將一個字元,寫入某檔案內,它的使用格式如下: int putc(int ch,FILE * fp); ch代表所欲輸出的字元。 fp檔案指標。 此函數如果執行成功,它的傳回值是ch字元 值。如果執行失敗,它的傳回值是EOF。
getc( ) getc( )函數的主要目的是從某一個檔案中,讀取一個字元,它的使用格式如下: int getc(FILE *fp); 當執行getc( )函數成功時,傳回值是所讀取 的字元,如果所讀取的是檔案結束字元, 則此值是EOF。而在stdio.h內,EOF值是 -1。
範例1(計算此程式的字元數量) #include <stdio.h> int main() { FILE *fp; int count = 0; fp = fopen("13_1.c","r"); if(fp == NULL) printf("\n******open file error*****"); exit(1); } while ( getc(fp) != EOF ) count++; printf("The number of character for 13_1.c is %d\n", count); fclose(fp); return 0;
int main( int argc, char *argv[]) C/C++語言中的main函數,經常帶有參數argc,argv,如下: int main( int argc, char *argv[]) argc 是指命令行輸入參數的個數。 argv是儲存了所有的命令行參數。
範例2(複製檔案到另一檔案) #include <stdio.h> int main( int argc, char *argv[]) { FILE *fp1, *fp2; char chr; if(argc != 3) printf("\ninput data error"); exit(1); } fp1 = fopen( argv[1], "r"); fp2 = fopen( argv[2], "w"); while ( (chr = getc(fp1) ) != EOF ) fputc (chr, fp2 ) ; fclose(fp1); fclose(fp2); return 0;
fprintf( ) fprintf( )主要目的是供你將資料,以格式化方式寫入某檔案內 Example: fprintf(fp,"%d\n",var); fprintf(fp,"\2: The average is %6.2f",average);
fscanf() fscanf( ) 主要的目的是讓我們從某個檔案讀取資料 fscanf( fp , "……." , ………); fscanf( )函數和scanf( )函數兩者之間最大的差別在,scanf( )函數主要用於從鍵盤輸入讀取資料,fscanf( )函數則是從fp檔案指標所指的檔案讀取資料。 Example: fscanf(fp,"%d",&var)
feof( ) / ferror( ) feof( )主要功能是測試在讀取資料時,是否已經讀到檔案末端位置: int feof(fp); 若讀取資料的位置是在檔案末端,則傳回非零數 值,否則傳回零。 ferror( )主要用於測試前一個有關檔案指標的操作是否正確: int ferror(fp); 如果前一個函數對有關檔案指標的操作,有錯誤,則傳回值是非零數值,否則傳回零值。
fwrite( ) / fread( ) fwrite( )主要目的是將某個資料緩衝區內容,寫入檔案指標所指的二元檔檔案內。 int fwrite(void *buffer, int num_byte, int count, FILE *ftp); fread( )主要目的是將某二元檔檔案內的資料, 讀入資料緩衝區內。 int fread(void *buffer, int num_byte, int count, FILE *fp);
fseek( ) / rewind( ) fseek( )主要目的是設定所要讀取資料的位置,以達隨機位置讀取檔案資料的目的。 int fseek(fp, long num_byte, int origin); origin有三種格式: 0:表示從檔案開頭位置 1:表示從目前位置 2:表示從檔案末端位置 num_byte為與origin的距離。 rewind( ) 功能在於將檔案指標移至檔案的開 頭。
struct 結構與陣列很類似,因為兩者都是將一些資 料連接組合在一起,以下為兩者的比較。 陣列 : 相同型態資料之集合. 結構(struct) :可將不同型態的資料組合在一起 結構(struct)為結構(structure)的縮寫 結構中的項目通常稱為成員(member)。
Struct 能夠結合多個彼此相關的變數在一個名稱之下,且可以包含數個不同資料型態的變數。 結構是一種使用者自定的型態,它可將不同的資料型態串在一起。 舉例:「學生個人資料表」, 學號(字串型態) 姓名(字串型態) 成績(整數型態)
struct 結構名稱標籤 { 資料型態 變數1; 資料型態 變數2; ‧‧‧‧‧‧‧‧ }; 100 98753506 Wendy Lo ID[10] FirstName[10] LastName[10] grade Student struct 結構名稱標籤 { 資料型態 變數1; 資料型態 變數2; ‧‧‧‧‧‧‧‧ }; struct 結構名稱 變數1,變數2,…,變數m; struct Student { char ID[10]; char FirstName[10]; char LastName[10]; int grade; }; struct Student stu;
上頁敘述會在記憶體裡配置空間出來讓 student 結構變數使用,記憶體的配置情況如下: ID Student FirstName LastName grade
將結構型態與結構變數的宣告寫在同一個敘述 struct 結構名稱 { 資料型態 欄位名稱1; 資料型態 欄位名稱2; . 資料型態 欄位名稱n; }變數1,變數2,…,變數m;
利用小數點(.)來存取結構變數中的成員。 結構變數名稱.成員名稱 例如: stu.ID = “96753017” stu.Firstname= “Wendy” stu.LastName = “Lo”; stu.grade= 100;
初值的設定 範例 : struct Student /* 定義結構mydata */ { char ID[10]; char FirstName[10]; char LastName[10]; int grade; }; struct Student stu={“96753017”,”Wendy”,”Lo”,0 }; /* 宣告結構Student型態之變數stu,並設定結構內欄位的初值 */
結構陣列 結構變數亦可以用宣告陣列,語法如下: struct 結構名稱 結構陣列變數名稱[陣列大小]; Ex: struct Student { char ID[10]; char FirstName[10]; char LastName[10]; int grade; }; struct Student stu[100]; /*結構變數的宣告*/
#include<stdio.h> #define MAX 2 int main(void) { int i; struct data /*定義結構data */ char name[10]; int math; }student[MAX]; for(i=0;i<MAX;i++) /*宣告結構陣列student */ printf("學生姓名:"); gets(student[i].name); printf("數學成績:"); scanf("%d",&student[i].math); fflush(stdin); /*清空緩衝區內的資料 */ } for(i=0;i<MAX;i++) /*輸出結構陣列的內容*/ printf("%s的數學成績=%d\n",student[i].name,student[i].math); system("pause"); return 0;}
結構指標 結構指標 struct 結構名稱 * 結構指標變數名稱; 結構變數也是指標的一種,只是它是指向結構的指標,而非指向一般變數。 宣告格式如下: struct 結構名稱 * 結構指標變數名稱; Ex: struct student /* 定義結構mydata */ { char name[15]; /* 各成員(欄位)的內容 */ char id[10]; int math; int eng; }; struct student * prt; /*結構變數的宣告*/ ptr=&student; /*將指標指向此結構變數 student*/
結構指標變數的使用 使用指標來存取結構變數內的成員(欄位)時,要 利用「->」來連接欲存取的成員,如下面格式: 結構指標名稱 -> 結構變數成員 Ex: 如前一頁所宣告的結構變數student及指標ptr為例, ptr->math即代表了指向結構變數student的math成員。
#include<stdio. h> #include<stdlib #include<stdio.h> #include<stdlib.h> int main(void) { struct data char name[10]; int eng; }student; struct data *ptr; ptr=&student; printf("學生姓名:"); gets(ptr->name); printf("英文成績: "); scanf("%d",&ptr->eng); printf("英文成績=%d",ptr->eng); system("pause"); return 0; }
巢狀結構 結構可以存放不同的資料型態,所以在結構中也可以擁有另一個結構,如此就稱為巢狀結構(nested structure). 格式如下: { /*結構1的成員*/ }; struct 結構2 /*結構2的成員*/ struct 結構1 變數名稱;
#include<stdio. h> #include<stdlib #include<stdio.h> #include<stdlib.h> int main(void) { struct date /*define date*/ int month; int day; }; struct student /*define nested structure---student*/ char name[10]; int math; struct date birthday; }s1={“David Li",80, {2,10}}; printf("student name:%s\n",s1.name); printf("birthday:%d month, %d day\n",s1.birthday.month,s1.birthday.day); printf("math grade:%d\n",s1.math); return 0; }
Merry Christmas
習題 題目: 有一個表示學生國英數成績結構陣列如右所附。 請完成二個函數,其原型分別為 void happy_birthday(struct data [], int); int max_grade(struct data []); 其功能可以 (1)列印12月份生日的學生姓名; (2)傳回全班平均成績最高的學生的索引值 。 請完成函數,並依題目輸出要求,測試函數,印出結果。 題目輸入: 八個學生資料如右所附。 題目輸出: (1)印出本月壽星的學生姓名 (2)印出全班平均成績最高的學生姓名 struct day { int yy, mm, dd; /* 年、月、日 */ }; struct data { char name[20]; /* 姓名 */ struct day birthday; /* 生日 */ int chi, math, eng; /* 國文、數學 與 英文成績 */ }; struct data student[8] = {{"Marry Hu", {77, 2, 3}, 89, 90, 79}, {"Tom Chen", {78, 12, 13}, 79, 69, 88}, {"Billy Wu", {77, 1, 30}, 81, 54, 66}, {"John Hsu", {77, 7, 22}, 69, 49, 70}, {"Tim Huang", {77, 12, 8}, 90, 62, 83}, {"Marry Chen", {78, 5, 27}, 78,93, 91}, {"Tomas Chu", {77, 5, 18}, 80, 50, 68}, {"Ann Wang", {77, 9, 21},100,100, 100}};