【實驗結論】
Part1: 驗證性實驗
• 驗證性實驗二
將line29的代碼做修改后的程序源碼:

1 // 從文本文件file1.dat中讀取數據,找出最高分和最低分學生信息,並輸出在屏幕上 2 #include <stdio.h> 3 #include <stdlib.h> 4 5 #define N 10 6 7 // 定義一個結構體類型STU 8 typedef struct student { 9 int num; 10 char name[20]; 11 int score; 12 }STU; 13 14 int main() { 15 STU st, stmax, stmin; 16 int i; 17 FILE *fp; 18 19 // 以只讀文本方式打開文件file1.dat 20 fp = fopen("file1.dat", "r"); 21 if( !fp ) { // 如果打開失敗,則輸出錯誤提示信息,然后退出程序 22 printf("fail to open file1.dat\n"); 23 exit(0); 24 } 25 26 stmax.score = 0; // 先假定最高分是0,后面如發現比當前最高分還高的分數,就更新最高分 27 stmin.score = 100; // 先假定最低分是100分,后面如發現比當前最低分更低的分數,就更新最低分 28 29 while(!feof(fp)){ 30 fscanf(fp, "%d %s %d", &st.num, st.name, &st.score); // 從fp指定的文件中格式化讀取一個學生信息 31 32 if(st.score > stmax.score) 33 stmax = st; 34 else if(st.score < stmin.score) 35 stmin = st; 36 } 37 38 fclose(fp); 39 40 printf("最高分學生信息: %5d%15s%5d\n", stmax.num, stmax.name, stmax.score); 41 printf("最低分學生信息: %5d%15s%5d\n", stmin.num, stmin.name, stmin.score); 42 43 return 0; 44 }
運行結果截圖:
由運行結果可知,在不知道總人數的情況下,將line29的代碼改為 while( !feof(fp) ) 之后,運行結果仍然是正確的。
這是因為,函數feof()是用於判斷文件是否到了結束標志,函數的返回值若是非0的數,則說明文件指針已指向文件的結尾了。在這段代碼中結構體變量st會一直從文件中讀取數據,直到文件中的數據都已被訪問。
• 對比驗證性實驗3和驗證性實驗4的程序源碼及運行結果,總結比較二進制文件與文本文件的區別
▶ 驗證性實驗3:
程序源碼:

1 // 從文本數據文件file1.dat中讀入數據,按成績從高到低排序,將排序結果輸出到屏幕上,同時以文本方式存入文件file3.dat中。 2 #include <stdio.h> 3 #include <stdlib.h> 4 5 #define N 10 6 7 // 定義一個結構體類型STU 8 typedef struct student { 9 int num; 10 char name[20]; 11 int score; 12 }STU; 13 14 void sort(STU *pst, int n); // 函數聲明 15 16 int main() { 17 FILE *fin, *fout; 18 STU st[N]; 19 int i; 20 21 // 以只讀文本方式打開文件file1.dat 22 fin = fopen("file1.dat", "r"); 23 if( !fin ) { // 如果打開失敗,則輸出錯誤提示信息,然后退出程序 24 printf("fail to open file1.dat\n"); 25 exit(0); 26 } 27 28 // 從fin指向的數據文件file1.dat中讀取數據到結構體數組st 29 for(i=0; i<N; i++) 30 fscanf(fin, "%d %s %d", &st[i].num, st[i].name, &st[i].score); 31 32 fclose(fin); // 關閉fin指向的文件file1.dat 33 34 // 調用函數sort()對數組st中數據,按分數又高到低排序 35 sort(st, N); 36 37 // 以寫方式打開/創建文本文件file3.dat 38 fout = fopen("file3.dat", "w"); 39 if( !fout ) { // 如果打開失敗,則輸出錯誤提示信息,然后退出程序 40 printf("fail to open file1.dat\n"); 41 exit(0); 42 } 43 44 // 將排序后的數組st中數據輸出到屏幕,同時,也寫入文件file3.dat 45 for(i=0; i<N; i++) { 46 printf("%-6d%-10s%3d\n", st[i].num, st[i].name, st[i].score); 47 fprintf(fout, "%-6d%-10s%3d\n", st[i].num, st[i].name, st[i].score); 48 } 49 50 fclose(fout); // 關閉fout指向的文件file3.dat 51 52 return 0; 53 }
運行結果截圖:
▶ 驗證性實驗4:
程序源碼:

1 // 從文本數據文件file1.dat中讀入數據,按成績從高到低排序,並將排序結果輸出到屏幕上,同時,也以二進制方式存入文件file4.dat中。 2 #include <stdio.h> 3 #include <stdlib.h> 4 5 #define N 10 6 7 // 定義一個結構體類型STU 8 typedef struct student { 9 int num; 10 char name[20]; 11 int score; 12 }STU; 13 14 void sort(STU *pst, int n); // 函數聲明 15 16 int main() { 17 FILE *fin, *fout; 18 STU st[N]; 19 int i; 20 21 // 以只讀文本方式打開文件file1.dat 22 fin = fopen("file1.dat", "r"); 23 if( !fin ) { // 如果打開失敗,則輸出錯誤提示信息,然后退出程序 24 printf("fail to open file1.dat\n"); 25 exit(0); 26 } 27 28 // 從fin指向的數據文件file1.dat中讀取數據到結構體數組st 29 for(i=0; i<N; i++) 30 fscanf(fin, "%d %s %d", &st[i].num, st[i].name, &st[i].score); 31 32 fclose(fin); // 關閉fin指向的文件file1.dat 33 34 // 調用函數sort()對數組st中數據,按分數由高到低排序 35 sort(st, N); 36 37 // 以寫方式打開/創建二進制文件file4.dat 38 fout = fopen("file4.dat", "wb"); 39 if( !fout ) { // 如果打開失敗,則輸出錯誤提示信息,然后退出程序 40 printf("fail to open file1.dat\n"); 41 exit(0); 42 } 43 44 // 將排序后的數組st中數據輸出到屏幕 45 for(i=0; i<N; i++) 46 printf("%-6d%-10s%3d\n", st[i].num, st[i].name, st[i].score); 47 48 // 將排序后的數組st中數據寫到二進制文件file4.dat 49 fwrite(st, sizeof(STU), N, fout); // 將從地址st開始的sizeof(STU)×N個字節信息寫入fout指向的文件file4.dat中 50 51 fclose(fout); // 關閉fout指向的文件file4.dat 52 53 return 0; 54 } 55 56 57 // 函數功能描述:對pst指向的n個STU結構體數據進行排序,按成績數據項由高到底排序 58 // 排序算法:冒泡法 59 void sort(STU *pst, int n) { 60 STU *pi, *pj, t; 61 62 for(pi = pst; pi < pst+n-1; pi++) 63 for(pj = pi+1; pj < pst+n; pj++) 64 if(pi->score < pj->score) { 65 t = *pi; 66 *pi = *pj; 67 *pj = t; 68 } 69 70 71 }
運行結果截圖:
二進制文件與文本文件的區別總結:
|
數據類型 |
數據長度 |
讀取軟件 |
操作系統對換行符'\n‘’的處理 |
文本文件 |
只能以字符形式儲存變量。 |
固定長度。如ASCII碼每條數據(每個字符)都是1個字節 |
文本文件編輯器 |
操作系統會對'\n'進行一些隱式變換 |
二進制文件 |
可以存儲char/int/short/long/float/等各種變量值。 |
不固定。如short占兩個字節,int占四個字節,float占8個字節。 |
不同類型的需要特別解碼器。 |
操作系統不會對'\n'進行隱式變換。 |
二進制文件file4.dat中讀出數據:
程序源碼:

1 #include<stdio.h> 2 #include<stdlib.h> 3 typedef struct student { 4 int num; 5 char name[20]; 6 int score; 7 }STU; 8 int main() 9 { 10 FILE *fp; 11 STU st[10]; 12 int i=0,j; 13 if((fp=fopen("file4.dat","rb"))==NULL) 14 { 15 printf("Failed to open file"); 16 exit(0); 17 } 18 while(!feof(fp)) 19 { 20 if(fread(&st[i],sizeof(struct student),1,fp) == 1) { 21 printf("%-6d %-10s %3d\n", st[i].num, st[i].name, st[i].score); 22 i++; 23 } 24 } 25 26 fclose(fp); 27 return 0; 28 }
運行結果截圖:
這里運行結果的最后一行有什么意義?求解答!
↑這里根據老師的建議已經修改了代碼,完美運行!( ̄▽ ̄)/ 順便做個對比吧。
修改前while語句內的代碼:
1 fread(&st[i],sizeof(struct student),1,fp); 2 printf("%-6d %-10s %3d\n", st[i].num, st[i].name, st[i].score); 3 i++;
運行結果截圖:
程序源碼:

1 #include <stdio.h> 2 #include <string.h> 3 #include <stdlib.h> 4 const int N = 10; 5 6 // 定義結構體類型struct student,並定義其別名為STU 7 typedef struct student { 8 long int id; 9 char name[20]; 10 float objective; /*客觀題得分*/ 11 float subjective; /*操作題得分*/ 12 float sum; 13 char level[10]; 14 }STU; 15 16 // 函數聲明 17 void input(STU s[], int n); 18 void output(STU s[], int n); 19 void process(STU s[], int n); 20 21 int main() { 22 STU stu[N]; 23 24 printf("錄入%d個考生信息: 准考證號,姓名,客觀題得分(<=40),操作題得分(<=60)\n", N); 25 input(stu, N); 26 27 printf("\n對考生信息進行處理: 計算總分,確定等級\n"); 28 process(stu, N); 29 30 printf("\n打印考生完整信息: 准考證號,姓名,客觀題得分,操作題得分,總分,等級\n"); 31 output(stu, N); 32 33 return 0; 34 } 35 36 // 錄入考生信息:准考證號,姓名,客觀題得分,操作題得分 37 void input(STU s[], int n) { 38 FILE *fin; 39 // 以只讀文本方式打開文件examinee.txt 40 fin=fopen("examinee.txt","r"); 41 if( !fin ) { // 如果打開失敗,則輸出錯誤提示信息,然后退出程序 42 printf("Failed to open file\n"); 43 exit(0); 44 } 45 int i; 46 for(i=0;i<n;i++) 47 { 48 // 從fin指定的文件中格式化讀取學生信息 49 if(fscanf(fin,"%ld %s %f %f",&s[i].id,s[i].name,&s[i].objective,&s[i].subjective)==0) 50 printf("讀取錯誤!"); 51 } 52 fclose(fin); 53 } 54 55 //輸出考生完整信息: 准考證號,姓名,客觀題得分,操作題得分,總分,等級 56 void output(STU s[], int n) { 57 int i; 58 FILE *fout; 59 // 以只寫文本方式打開/創建文件result.txt 60 fout=fopen("result.txt","w"); 61 //在屏幕和文檔中打印表頭 62 printf("准考證號 姓名 客觀題得分 操作題得分 總分 等級\n"); 63 fprintf(fout,"准考證號 姓名 客觀題得分 操作題得分 總分 等級\n"); 64 for(i=0;i<n;i++)// 格式化輸出學生信息到fout指定的文件中 65 { 66 printf(" %-9ld%-10s%-13.2f%-10.2f%-7.2f%-8s\n", 67 s[i].id,s[i].name,s[i].objective,s[i].subjective,s[i].sum,s[i].level); 68 fprintf(fout," %-9ld %-10s%-13.2f%-10.2f%-7.2f%-8s\n", 69 s[i].id,s[i].name,s[i].objective,s[i].subjective,s[i].sum,s[i].level); 70 } 71 fclose(fout); 72 } 73 74 // 對考生信息進行處理:計算總分,排序,確定等級 75 void process(STU s[], int n) { 76 int i,j,k; 77 int a1,a2; //用來表示10%和50%兩個分界點 78 a1=(int)(N*0.1),a2=(int)(N*0.5); 79 STU temp; 80 for(i=0;i<n;i++) 81 s[i].sum=s[i].objective+s[i].subjective; 82 for(j=0;j<n-1;j++) 83 for(k=0;k<n-j-1;k++) 84 if(s[k].sum<s[k+1].sum) 85 { 86 temp=s[k]; 87 s[k]=s[k+1]; 88 s[k+1]=temp; 89 } 90 for(i=0;i<a1;i++) 91 strcpy(s[i].level,"優秀"); 92 for(i=a1;i<a2;i++) 93 strcpy(s[i].level,"合格"); 94 for(i=a2;i<n;i++) 95 strcpy(s[i].level,"不合格"); 96 } 97
運行結果截圖:
Part3: 拓展綜合應用
程序源碼:

1 #include<stdio.h> 2 #include<time.h> 3 #include<stdlib.h> 4 #include<string.h> 5 #define N 80 6 typedef struct{ 7 int number; 8 char stu_number[10]; 9 char name[10]; 10 char class_name[20]; 11 int flag; //用於標志是否已被抽取到,防止重復抽取相同數據 12 }STU; 13 int main(){ 14 FILE *fin,*fout; 15 int i,j; 16 STU list[N]; 17 18 //數據讀入 19 fin=fopen("list.txt","r"); 20 if(!fin){ 21 printf("Failed to open file"); 22 exit(0); 23 } 24 for(i=0;i<N;i++){ 25 fscanf(fin,"%d %s %s %s", &list[i].number, 26 list[i].stu_number, 27 list[i].name, 28 list[i].class_name); 29 list[i].flag=0; 30 } 31 fclose(fin); 32 33 //確定導出文件名 34 char buff[20]; 35 struct tm *t; 36 time_t tt; 37 time(&tt); 38 //調用 localtime函數查閱系統日期 39 t=localtime(&tt); 40 //調用 strftime函數將時間日期轉換為字符串 41 if (!strftime(buff, sizeof buff, "%Y-%m-%d",t)) 42 //"%Y-%m-%d"這里有很多C99標准的代碼無法使用 43 puts("strftime failed"); 44 strcat(buff,".txt"); //字符串連接函數--在字數串數組的結尾加上文件擴展名 45 46 //數據抽取、導出 47 srand(time(NULL)); 48 fout=fopen(buff,"w"); 49 if(!fout){ 50 printf("Failed to open file"); 51 exit(0); 52 } 53 i=0; 54 printf("Five lucky people:\n"); 55 while(i++<5){ 56 //生成隨機數 57 j=rand()%N; 58 if(list[j].flag==0){ 59 fprintf(fout,"%02d %10s %-8s %-20s\n",list[j].number, 60 list[j].stu_number, 61 list[j].name, 62 list[j].class_name); 63 printf("%-2d %10s %-8s %-20s\n", list[j].number, 64 list[j].stu_number, 65 list[j].name, 66 list[j].class_name); 67 list[j].flag=1; //對已經抽到的成員進行標識 68 } 69 else{ 70 i--; 71 continue; 72 } 73 } 74 fclose(fout); 75 return 0; 76 }
運行結果截圖:
【實驗總結與體會】
從文件讀取數據的價值在程序需要大容量數據輸入時體現得尤為突出,正好解決了在實驗六中在調試程序時需要反復輸入多組數據的煩惱。
實驗中踩的坑: (1)在進行二進制讀寫操作時,一定要使用fread(),fwrite()函數而不能使用fscanf(),fprintf()函數,否則會出現亂碼或達不到預期的效果;
(2)在學習調用localtime()和strftime()函數時花了不少時間,也失敗了很多次,盡管最后成功了還是有一些細節沒有完全弄懂,還是需要多查資料看別人寫的規范的代碼;
(3)從二進制文件讀取代碼時末尾出現的亂碼不知道是哪里出了問題。
評論博客地址
https://www.cnblogs.com/1256096713a/