实验七
【实验结论】
Part1: 验证性实验
验证性实验1:已知文件file1.txt已经存在,将file1.txt中小写字母转换成大写后,另存为file2.txt。
运行结果:
更改文件路径,电脑的C盘下出现文件file3.txt。
fout = fopen("file2.txt", "w"); ↓↓↓ 改为 fout = fopen("C:\\test\\file3.txt", "w");
注意:对于以"w"方式打开的文件,如果原先文件不存在,那么就新建一个;如果文件已经存在,那么,新写入的数据会覆盖文件中原来的内容。如果不想覆盖可以以"a"方式打开。
验证性实验2:已知文本数据文件file1.dat,从文件file1.dat中读入数据,找出最高分和最低分学生信息,并输出在屏幕上。
// 从文本文件file1.dat中读取数据,找出最高分和最低分学生信息,并输出在屏幕上 #include <stdio.h> #include <stdlib.h> #define N 10 // 定义一个结构体类型STU typedef struct student { int num; char name[20]; int score; }STU; int main() { STU st, stmax, stmin; int i; FILE *fp; // 以只读文本方式打开文件file1.dat fp = fopen("file1.dat", "r"); if( !fp ) { // 如果打开失败,则输出错误提示信息,然后退出程序 printf("fail to open file1.dat\n"); exit(0); } stmax.score = 0; // 先假定最高分是0,后面如发现比当前最高分还高的分数,就更新最高分 stmin.score = 100; // 先假定最低分是100分,后面如发现比当前最低分更低的分数,就更新最低分 while( !feof(fp) ) { fscanf(fp, "%d %s %d", &st.num, st.name, &st.score); // 从fp指定的文件中格式化读取一个学生信息 if(st.score > stmax.score) stmax = st; else if(st.score < stmin.score) stmin = st; } fclose(fp); printf("最高分学生信息: %5d%15s%5d\n", stmax.num, stmax.name, stmax.score); printf("最低分学生信息: %5d%15s%5d\n", stmin.num, stmin.name, stmin.score); return 0; } // 这是《C语言程序设计教程学习指导》「2.10 文件」中的实验,细微处做了微调 // 这个源代码没有考虑多个高分或多个低分的情形。
运行结果:
如果事先不知道学生人数,则程序应该如何修改?尝试对line29做如下修改:
for(i=0; i<N; i++) ↓↓↓ 改为 while( !feof(fp) )
再次运行时结果同上。
在事先不知道学生人数,且最高 / 最低分可能不止一个的情况下,对程序进行改写:
// 从文本文件file1.dat中读取数据,找出最高分和最低分学生信息,并输出在屏幕上 #include <stdio.h> #include <stdlib.h> // 定义一个结构体类型STU typedef struct student { int num; char name[20]; int score; }STU; int main() { STU st[20], stmax[5], stmin[5]; int i=0,max,min,a,b=0,c=0; FILE *fp; // 以只读文本方式打开文件file1.dat fp = fopen("file1.dat", "r"); if( !fp ) { // 如果打开失败,则输出错误提示信息,然后退出程序 printf("fail to open file1.dat\n"); exit(0); } max = 0; // 先假定最高分是0,后面如发现比当前最高分还高的分数,就更新最高分 min = 100; // 先假定最低分是100分,后面如发现比当前最低分更低的分数,就更新最低分 while( !feof(fp) ) { fscanf(fp, "%d %s %d", &st[i].num, st[i].name, &st[i].score); // 从fp指定的文件中格式化读取学生信息 if(st[i].score > max) max = st[i].score; else if(st[i].score < min) min = st[i].score; i++; } a=i; for(i=0;i<a;i++){ if(st[i].score == max) stmax[b++] = st[i]; else if(st[i].score == min) stmin[c++] = st[i]; } fclose(fp); printf("最高分学生信息:\n"); for(i=0;i<b;i++) printf("%5d%15s%5d\n", stmax[i].num, stmax[i].name, stmax[i].score); printf("最低分学生信息:\n"); for(i=0;i<c;i++) printf("%5d%15s%5d\n", stmin[i].num, stmin[i].name, stmin[i].score); return 0; }
添加学生信息:
运行结果:
实现方式和之前做过的实验差不多,定义了结构体数组。
验证性实验3 & 验证性实验4
从文件file1.dat中读入数据,按成绩从高到低排序,将排序结果输出到屏幕上,同时也以文本 / 二进制方式存入文件file3.dat中。
运行结果:
屏幕上显示的内容相同。
文件中显示的内容不同,二进制文件中数据信息不直观可读。
二进制文件与文本文件的区别(来源于课件):
文本文件
数据以ASCⅡ码形式存储,也称ASCⅡ码文件 每个字节存放一个字符的ASCII码。
特点:存储量大、速度慢; 直观,便于对字符操作。
二进制文件
数据按其在内存中的存储形式原样存放。
特点:存储量小、速度快、便于存放中间结果;不直观。
写一个简单的程序,尝试从二进制文件file4.dat中读出数据,并在屏幕上显示,以此查看文件file4.dat的内容。
#include <stdio.h> #include <stdlib.h> #define N 10 typedef struct student { int num; char name[20]; int score; }STU; int main(){ FILE *fp; int i; STU st[N]; fp = fopen("file4.dat", "rb"); if( !fp ) { printf("fail to open file4.dat\n"); exit(0); } for(i=0; i<N; i++) fread(&st[i],sizeof(STU),N,fp); for(i=0; i<N; i++) printf("%-6d%-10s%3d\n",st[i].num,st[i].name,st[i].score); fclose(fp); return 0; }
运行结果:
注意:使用fprintf()和fscanf()写入和读出格式化数据时,用什么格式写入文件,就一定要以什么格式从文件读出,否则会造成数据出错。如果不需要转换数据的内外形式,可以直接使用块读写函数fwrite()和fread()进行写入和读出。
Part2: 编程练习
#include <stdio.h> #include <string.h> #include <stdlib.h> const int N = 10; // 定义结构体类型struct student,并定义其别名为STU typedef struct student { long int id; char name[20]; float objective; /*客观题得分*/ float subjective; /*操作题得分*/ float sum; char level[10]; }STU; // 函数声明 void input(STU s[], int n); void output(STU s[], int n); void process(STU s[], int n); int main() { STU stu[N]; printf("录入%d个考生信息: 准考证号,姓名,客观题得分(<=40),操作题得分(<=60)\n", N); input(stu, N); printf("\n对考生信息进行处理: 计算总分,确定等级\n"); process(stu, N); printf("\n打印考生完整信息: 准考证号,姓名,客观题得分,操作题得分,总分,等级\n"); output(stu, N); return 0; } // 从文本文件examinee.txt读入考生信息:准考证号,姓名,客观题得分,操作题得分 void input(STU s[], int n) { FILE *fp; int i; fp = fopen("examinee.txt","r"); if(fp == NULL){ printf("fail to open examinee.txt\n"); exit(0); } for(i=0; i<N; i++) fscanf(fp,"%ld %s %f %f",&s[i].id,s[i].name,&s[i].objective,&s[i].subjective); fclose(fp); } // 输出考生完整信息: 准考证号,姓名,客观题得分,操作题得分,总分,等级 // 不仅输出到屏幕上,还写到文本文件result.txt中 void output(STU s[], int n) { FILE *fp; int i; fp = fopen("result.txt","w"); if(fp == NULL){ printf("fail to open result.txt\n"); exit(0); } for(i=0; i<N; i++){ printf("%ld %s %.2f %.2f %.2f %s\n",s[i].id,s[i].name,s[i].objective,s[i].subjective,s[i].sum,s[i].level); fprintf(fp,"%ld %s %.2f %.2f %.2f %s\n",s[i].id,s[i].name,s[i].objective,s[i].subjective,s[i].sum,s[i].level); } fclose(fp); } // 对考生信息进行处理:计算总分,排序,确定等级 void process(STU s[], int n) { int i,j; STU temp; for(i=0;i<n;i++) s[i].sum=s[i].objective+s[i].subjective; for(i=0;i<n-1;i++){ for(j=0;j<n-1-i;j++){ if(s[j].sum<s[j+1].sum){ temp=s[j]; s[j]=s[j+1]; s[j+1]=temp; } } } for(i=0;i<n;i++){ if(i<n*0.1) strcpy(s[i].level,"优秀"); else if(i>=n*0.1 && i<n*0.5) strcpy(s[i].level,"合格"); else strcpy(s[i].level,"不合格"); } }
运行结果:
Part3: 拓展综合应用
已知班级学生名单信息存在文本文件list.txt中,编程实现随机抽点5位学生信息,显示在屏幕上,同时,写入以系统日期命名的文件中。
#include <stdio.h> #include <stdlib.h> #include <time.h> #include <math.h> #define N 80 int main(){ FILE *fin,*fout; int i,j,num[5]; char str[N][64]; time_t t; char strtime[60]={0}; struct tm *info; t = time(0); info = localtime( &t ); //读取系统日期 strftime(strtime, 60, "%Y-%m-%d.txt", info); //将系统日期转换为文件名形式的字符串 fin = fopen("list.txt","r"); if(!fin){ printf("fail to open list.txt"); exit(0); } for(i=0; i<N; i++) fgets(str[i],64,fin); //从文件中整行读取字符串 srand(time(0)); //使用系统时间的值作为随机种子 num[0] = rand() % N; //取5个0 ~ N-1的随机数 for(i=1; i<5; i++){ num[i] = rand() % N; for(j=0; j<i; j++){ //避免重复 if(num[i] == num[j]){ num[i] = rand() % N; j = 0; } } } fout = fopen(strtime, "w"); //文件名是变量时,可以用地址(指针)传入 if(!fout){ printf("fail to open %s",strtime); exit(0); } for(i=0; i<5; i++){ printf("%s\n",str[num[i]]); fprintf(fout,"%s\n",str[num[i]]); } fclose(fin); fclose(fout); return 0; }
运行结果:
总结:
1.用rand()%N生成一个小于N的随机数的操作是在百科园第八章看到的,感觉还挺好用的。一开始用rand()函数的时候没有写srand(time(0)),导致每次运行时结果没有变化,查阅之后才知道可以使用系统时间作为随机种子。
2为了弄清localtime()和strftime()函数的使用方法查看了不少网站,大致明白了调用的格式,细节方面还没太搞懂。
3.fopen()函数中如何用变量做文件名也是我困惑好久的地方,看到了几种方法还是把".txt"直接放在strftime()函数里比较方便,但这里我有个小疑问,我原来这一行写的是:
strftime(strtime, 60, "%Y-%m-%d %H:%M:%S.txt", info); ↓↓↓ 改为 strftime(strtime, 60, "%Y-%m-%d.txt", info);
但这个时候运行显示文件无法正常打开,我找了好久的问题最后把后面部分删掉就正常了。这里出现问题的原因是什么呢?
4.写避免重复点名的时候忘了用标识法,就写成了循环,后来看了同学的才发现用标识更为简洁一些。
5.可能程序中还有些小错误,欢迎指出。
【实验总结与体会】
1.这次实验学习了有关文件的内容。当需要输入和输出大量数据时,文件可以替代键盘的输入,还能够进行数据的保存,起到相当重要的作用。
2.关于文件的函数的调用方法和注意事项在前面也写到了一些,还是要通过多运用来熟悉它们。通过文件指针来操作文件的方法虽然没有在实验里用到,如果能够掌握的话应该会对编程更有帮助。
3.想到这是最后一次实验,就尽力地来完成了,虽然因此花费了好多时间,但还是值得的。
4.希望大家期末都能取得好成绩!