410 likes | 531 Views
Chapter 8. 檔案處理 (File Processing). 什麼是檔案處理?. 當程式結束時,所有資料都會消失 「檔案」可以將資料永久保存於儲存硬體中 硬碟、軟碟、光碟、磁帶 … 為了資料的保存、及存取 檔案的處理是必要的 File 是由位元組成的循序資料流,以 EOF (end of file) 來結束 檔案被開啟後,資料流就會和檔案結合在一起. 開啟檔案 (1/2). FILE *fopen(const char * 檔案名稱 , const char * 模式 ). 開啟檔案 (2/2).
E N D
Chapter 8 檔案處理 (File Processing)
什麼是檔案處理? • 當程式結束時,所有資料都會消失 • 「檔案」可以將資料永久保存於儲存硬體中 • 硬碟、軟碟、光碟、磁帶… • 為了資料的保存、及存取 • 檔案的處理是必要的 • File是由位元組成的循序資料流,以EOF (end of file)來結束 • 檔案被開啟後,資料流就會和檔案結合在一起
開啟檔案 (1/2) • FILE *fopen(const char *檔案名稱, const char *模式)
開啟檔案 (2/2) • 檔案開啟後會傳回一個指向FILE結構的指標,欲對該檔案執行動作,則對該指標執行 • b可以和其他模式並用,而且’+’的位置不影響其意義:w+b = wb+ • 當使用w+, r+, a+ 來存取檔案時,在讀、寫模式切換時要注意指標的位置 • fsetpos, fseek, rewind • fopen 若開檔成功,則會傳回指向FILE結構的指標,反之則傳回NULL • 開檔時,仍要檢查是否開啟成功
關閉檔案 • 檔案使用結束,或程式結束時必須將所開啟之檔案全部關閉 • 確保所有緩衝區軍備清除,且可以繼續使用,以及檔案能正確的關閉 • int fclose(FILE *stream) • 若關檔成功,則傳回0
開、關檔案範例 (1/2) #include <stdio.h> int main(void) { FILE *in; char filename[80], buff[80]; printf("Enter name of file to open: "); gets(filename); if((in = fopen(filename, "r")) == NULL){ //檢查開檔是否成功 printf("Error opening file: %s\n", filename); exit(0); } printf("==== Contents of input file ====\n"); while(fgets(buff, 80, in) != NULL){ printf("%s", buff); } fclose(in); //檔案用完後,關閉該檔案 return 0; }
開、關檔案範例 (2/2) Enter name of file to open: eha2.c ==== Contents of input file ==== HARRY POTTER AND THE CHAMBER OF SECRETS by J. K. Rowling (this is BOOK 2 in the Harry Potter series) Original Scanned/OCR: Friday, April 07, 2000 v1.0 (edit where needed, change version number by 0.1) C H A P T E RR O N E THE WORST BIRTHDAY … … …
讀取檔案資料 (1/2) • int fgetc(FILE *檔案指標); • 從檔案中以int的格式將字元一個一個的讀入 • size_t fread(void *buffer, size_t 型態單元大小, size_t 數目, FILE *檔案指標); • 通常用於二進位檔 (binary file),從檔案目前位置讀入某種型態的東西的緩衝區,讀完後檔案位置也會被更新 • char *fgets(char *buffer, int 數目, FILE *檔案指標); • 從檔案中讀入一字串,並存於緩衝區中 • 如遇到換行字元(\n),或是緩衝區大小少1,便會轉成字串,並在後面加上’\0’,’\n’也在字串中。 • 若成功則傳回字串,否則傳回NULL
讀取檔案資料 (2/2) • int fscanf(FILE *檔案, const char *格式, ...); • 依照所給的格式讀入資料,格式之指定以%開頭 (%d, %c, %s …) • 以%s為例:每當讀取到空白、Tab、換行時,便會視為動作結束 • 方便資料之處理,但要還原時困難(因為無法區隔空白、Tab、換行字元)
用fread()來改寫開、關檔案範例 (1/3) #include <stdio.h> int main(void) { FILE *in; char filename[80], buff[80]; int i; printf("Enter name of file to open: "); gets(filename); if((in = fopen(filename, "r")) == NULL){ //檢查開檔是否成功 printf("Error opening file: %s\n", filename); exit(0); } printf("==== Contents of input file ====\n"); while(fread(buff, sizeof(char), 80, in) != NULL){ printf("%s", buff); for(i = 0; i < 80; i++) buff[i] = 0;//為何要加這行 ? } fclose(in); //檔案用完後,關閉該檔案 return 0; }
用fread()來改寫開、關檔案範例 (2/3) Enter name of file to open: gcd.c ==== Contents of input file ==== #include <stdio.h> int gcd(int u, int v) { if(u < 0) u = -u; if(v < 0) v = -v; if(v == 0) return u; else return gcd(v, u%v); } int main(void) { int a, b, c; printf("Plz enter two non-zero number:"); scanf("%d %d", &a, &b); c = gcd(a, b); printf("gcd = %d\n", c); return 0; } scanf("%d %d", &a, &b); c = gcd //此版本沒有清buffer 在buffer中殘存的資料
用fread()來改寫開、關檔案範例 (3/3) Enter name of file to open: gcd.c ==== Contents of input file ==== #include <stdio.h> int gcd(int u, int v) { if(u < 0) u = -u; if(v < 0) v = -v; if(v == 0) return u; else return gcd(v, u%v); } int main(void) { int a, b, c; printf("Plz enter two non-zero number:"); scanf("%d %d", &a, &b); c = gcd(a, b); printf("gcd = %d\n", c); return 0; } //此版本有清buffer
用fgetc()來改寫開、關檔案範例 #include <stdio.h> int main(void) { FILE *in; char filename[80]; int i; printf("Enter name of file to open: "); gets(filename); if((in = fopen(filename, "r")) == NULL){ printf("Error opening file: %s\n", filename); exit(0); } printf("==== Contents of input file ====\n"); do{ i = fgetc(in); // 用法與getc()類似 printf("%c", i); }while(i != EOF); fclose(in); return 0; }
用fscanf()來改寫開、關檔案範例 (1/2) #include <stdio.h> int main(void) { FILE *in; char filename[80], buff[81]; printf("Enter name of file to open: "); gets(filename); if((in = fopen(filename, "r")) == NULL){ printf("Error opening file: %s\n", filename); exit(0); } printf("==== Contents of input file ====\n"); while(fscanf(in, "%s", buff) != EOF){ printf("%s\n", buff); } fclose(in); return 0; } 用來區隔每次抓到的字串
用fscanf()來改寫開、關檔案範例 (2/2) Enter name of file to open: gcd.c ==== Contents of input file ==== #include <stdio.h> int gcd(int u, int v) { if(u < 0) u = -u; ... ... ...
寫資料到檔案(1/2) • int fputc(int欲寫入字元, FILE *檔案); • 將字元(int)寫入檔案,寫入後檔案指標位置會增加 • size_t fwrite(const void *欲寫入字串, size_t 型態單元大小, size_t 數目, FILE *檔案指標); • 通常用於二進位檔 (binary file),從緩衝區寫入某種型態的東西到檔案目前位置,寫完後檔案位置也會被更新 • int fputs(const char *s, FILE *stream); • 將存於緩衝區中一字串寫入檔案 • 若成功則傳回最後一個被寫入的字元,否則傳回EOF
寫資料到檔案(2/2) • int fprintf(FILE *檔案, const char *格式, /* args*/ ...); • 和printf一樣,可以接受的參數個數並不一定,而不同的是這些參數要輸出指定的檔案 • 當所要輸入、輸出的檔案為stdin、stdout時,可以視為對標準輸入輸出做讀寫動作
Copy範例 (fgets(), fputs() – 1) #include <stdio.h> int main(void) { FILE *in, *out; char infile[80], outfile[80], buff[80]; printf("Enter name of file to open: "); gets(infile); //抓輸入檔案檔名 printf("Enter name of file to write: "); gets(outfile); //抓輸出檔案檔名 if((in = fopen(infile, “r”)) == NULL){ //檢查檔案是否可開成可讀 printf("Error opening file: %s\n", infile); exit(0); } if((out = fopen(outfile, “w”)) == NULL){ //檢查檔案是否可開成可寫 printf("Error opening file: %s\n", outfile); exit(0);
Copy範例 (fgets(), fputs() – 1) } while(fgets(buff, 80, in) != NULL){ fputs(buff, out); //把從輸入檔案抓到的資料寫到輸出檔 } fclose(in); //關閉檔案 fclose(out); return 0; }
fgets(), fputs() 與 stdin, stdout #include <stdio.h> //字串反轉範例 #include <string.h> int main(void) { char str[80], revstr[80]; int i = 0, j; printf("Please enter a string: "); fgets(str, 80, stdin); //對標準輸入抓值 j = strlen(str); while(str[i]){ revstr[j - i] = str[i]; i++; } revstr[j+1] = '\0'; fputs(revstr, stdout); //對標準輸出寫值 return 0; }
fgets/fputs 跟 gets/puts 的差別 • fgets 會檢查 buffer 的大小,所以不會產生 buffer overflow • gets 不會檢查 buffer 的大小,讀取時要確定 buffer 夠大 • gets 若遇到換行符號,會結束讀取,但不會把換行符號儲存到 buffer 裡面 • fgets 會把換行符號寫入 buffer • puts 在輸出時,會自己加上換行符號 • fputs 則無此特性
scanf 家族的格式化字串 • 可以包含下面三種 • 空白字元(Whitespace): 一個或者一個以上的輸入空白字元會被忽略 • 轉換指定詞: 用來解釋輸入的資料為那種型態 • 可以有四個欄位 • 星號: 代表轉換後的資料會被丟掉 • 寬度: 非負整數值,可以限制輸入的字元數;如果沒設定這個欄位,scanf 會一直讀取,直到遇到空白字元為止 • 修飾詞(Qualifier): h,l,L 用來修飾整數大小 • 轉換指定詞 : d,i,n, o,u,x, e,f,g • 其他字元: 其他字元若出現在格式化字串裡面,則輸入的資料必須跟這些字元吻合,scanf 函式才會進行轉換並且讀取的動作
fscanf & scanf 的範例 /* fscanf 的範例 */ #include <stdio.h> void main() { int a, b, c; if(fscanf(stdin, "%d %d", &a, &b) == 2 ) printf("已讀取到 2 個整數: %d, %d\n",a,b); fflush(stdin); if(scanf("%4d %4d %4d", &a, &b, &c)== 3) printf("讀取到 3 個整數:%d %d %d\n", a,b,c); } 執行結果: 12345 67890 已讀取到 2 個整數: 12345, 67890 12345 67890 讀取到 3 個整數:1234 5 6789
scan set 的運用 /* scan set 的範例 */ #include <stdio.h> void main() { char string[255]="test"; printf("請輸入字串 :"); scanf("%[aeiou]", string); printf("讀到 %s\n",string); fflush(stdin); printf("請輸入字串 :"); scanf("%[^aeiou]", string); printf("讀到 %s\n", string); } 執行結果: 請輸入字串 :aaaabbbbb 讀到 aaaa 請輸入字串 :bbbbcccaaa 讀到 bbbbccc
禁止字元的運用 /* *(禁止字元)用在 scanf 的範例 */ #include <stdio.h> void main() { int month, day, year; printf("請輸入日期(年/月/日) :"); scanf("%d%*c%d%*c%d", &year, &month, &day); printf("你輸入的是:\n"); printf("%d年%d月%d日 \n", year, month, day); } 執行結果: 請輸入日期(年/月/日) :2002-12-24 你輸入的是: 2002年12月24日 請輸入日期(年/月/日) :2002/12-24 你輸入的是: 2002年12月24日
printf 家族的格式化字串 • 可以包含下面五種 • flag: 控制格式化字串的轉換方式 • 最小列印欄位: 用來控制輸出的最小欄位 • 精準度: 用來控制小數的位數 • 修飾詞: (h,l,L)用來修飾轉換指定詞 • 轉換指定詞:
矩陣相乘檔案版 (1/7) #include <stdio.h> #include <stdlib.h> #include <ctype.h> int main(int argc, char *argv[]) { FILE *in1, *in2, *out; char file1[20], file2[20], file3[20], buff[20]; int i, j, k, m, n, l; int **matrix1, **matrix2, **matrix3, *row; if(argc != 5){ //判別傳入引數個數是否正確 printf("%s 是矩陣[m, n]和[n, l]相乘的運算\n", argv[0]); printf("輸入變數不足\n"); printf("Usage: %s m n n l\n", argv[0]); exit(0); }
矩陣相乘檔案版 (2/7) else if(strcmp(argv[2], argv[3])){ //檢查矩陣的維數是否合法 printf("%s 是矩陣[m, n]和[n, l]相乘的運算\n", argv[0]); printf("你輸入的矩陣維數不合法\n"); printf("Usage: %s m n n l\n", argv[0]); exit(0); } else{ //檢查輸入維數是否全部為數字 for(i = 1; i < 5; i++){ j = 0; while(argv[i][j] != '\0'){ if(!isdigit((int) argv[i][j])){ printf("%s is not a digit\n", argv[i]); exit(0); } j++; } } }
矩陣相乘檔案版 (3/7) m = atoi(argv[1]); n = atoi(argv[2]); l = atoi(argv[4]); printf(“請輸入第一個矩陣檔案:”); //輸入矩陣所在的檔案名稱 gets(file1); printf("請輸入第二個矩陣檔案:"); gets(file2); printf("請輸入第輸出矩陣檔案:"); gets(file3); if((in1 = fopen(file1, “r”)) == NULL){ //開啟檔案並且測試是否成功 printf("第一個矩陣檔案無法開啟\n"); exit(0); } if((in2 = fopen(file2, "r")) == NULL){ printf("第二個矩陣檔案無法開啟\n"); exit(0); } if((out = fopen(file3, "w")) == NULL){
矩陣相乘檔案版 (4/7) printf("輸出矩陣檔案無法開啟\n"); exit(0); } if((matrix1=(int **)malloc(sizeof(int *) *m) ) == NULL){ printf(“malloc 1 fail\n”); //要動態記憶體並且檢查 exit(0); } if((matrix2=(int **)malloc(sizeof(int *) *n) ) == NULL){ printf("malloc 2 fail\n"); exit(0); } if((matrix3=(int **)malloc(sizeof(int *) *m) ) == NULL){ printf("malloc 3 fail\n"); exit(0); } //get matrix 1 for(i = 0; i < m; i++){ //抓第一個矩陣的值 if((row=(int *)malloc(sizeof(int) *n) ) == NULL){
矩陣相乘檔案版 (5/7) printf("In matrix 1: %d row is not available\n", i); exit(0); } for(j = 0; j < n; j++){ fscanf(in1, "%s", buff); row[j] = atoi(buff); } matrix1[i] = row; } //get matrix 2 for(i = 0; i < n; i++){ //抓第二個矩陣的值 if((row=(int *)malloc(sizeof(int) *l) ) == NULL){ printf("In matrix 2: %d row is not available\n", i); exit(0); } for(j = 0; j < n; j++){ fscanf(in2, "%s", buff); row[j] = atoi(buff);
矩陣相乘檔案版 (6/7) } matrix2[i] = row; } //calculate matrix 3 for(i = 0; i < m; i++){ //要矩陣記憶體,並且計算矩陣值 if((row=(int *)calloc(l, sizeof(int)) ) == NULL){ printf("In matrix 3: %d row is not available\n", i); exit(0); } matrix3[i] = row; for(j = 0; j < l; j++) for(k = 0; k < n; k++) matrix3[i][j] += matrix1[i][k]*matrix2[k][j]; } for(i = 0; i < m; i++){ //把算完的值寫入檔案 for(j = 0; j < l; j++) fprintf(out, "%d ", matrix3[i][j]); fprintf(out, "\n");
矩陣相乘檔案版 (7/7) } //歸還所要的記憶體 for(i = 0; i < m; i++) free(matrix1[i]); for(i = 0; i < n; i++) free(matrix2[i]); for(i = 0; i < m; i++) free(matrix3[i]); free(matrix1); free(matrix2); free(matrix3); //關閉所開啟的檔案 fclose(in1); fclose(in2); fclose(out); return 0; }
檔案讀、寫注意事項 • 檔案的讀跟寫有很多函式可應用 • 依據需要選擇適當的函式 • 二進位檔:fread(), fwrite() • 一般文字檔: fgets(), fputs() • 字元:fgetc(), fputc() • 抓單字(word):fscanf(), fprintf()