10.2.2 用fclose 函数关闭数据文件;
为什么要关闭文件,当建立了一个文件,你就建立缓冲区和程序区域的流动。举例子,当输入到程序区域时候,你只有缓冲区满了才可以。如果你不关闭文件,直接关闭程序,你的缓冲区的数据将会丢失。所以需要先关闭文件,也就是先把缓冲区的数据转到数据区域的时候,再把程序关闭。
怎么调用关闭函数:
fclose(文件指针)
fclose(fp); 切断指针的指向,关闭文件。
10.3 顺序读写数据文件
顺序读写就是按照数据在文件的物理顺序,读文件的时候,自上而下。写文件的时候也是如此。对文件的顺序读写一般需要借助库函数实现。
10.3.1怎么向文件读写字符
看上面库函数的函数;
fgetc(fp);//fp:指向所需要操作的文件;文件读出 fputc(fp);//fp:指向所需要操作的文件;写入文件
举个例子:
/*从键盘上输入一些字符,逐个把他们送到磁盘上去, 直到用户输入一个“#”为止*/ #include <stdio.h> #include <stdlib.h> int main() { FILE *fp;//定义了一个指针变量,这个指针变量是用来指向文件的 char ch,filename[10];//定义了一个字符变量,和一个字符数组储存文件名 printf("please enter by-used filename: "); scanf("%s",filename); if((fp=fopen(filename,"w"))==NULL) { printf("无法打开文件\n"); exit(0); } //ch=getchar();//用来接受最后的回车符号 printf("请输入一个准备储存到磁盘的字符串(以#结束):");//输入一段字符串 ch=getchar();//接受字符串第一个字符; while(ch!='#') { fputc(ch,fp);//写入文件第一个字符 putchar(ch);//输出第一个字符到文件 ch=getchar();//得到下一个字符 } fclose(fp);//字符串全部输入完毕 ,关闭文件 putchar(10);//输出换行符 return 0; }
exit函数的头文件是stdlib.h;
能在磁盘中看到。
再看一个例子
/*将一个磁盘文件的信息复制到宁外一个磁盘文件上; 今让之前的file.dat 的数据放置到file2.dat 上。*/ #include <stdio.h> #include <stdlib.h> int main() { FILE *in,*out;//定义了两指针变量 char ch,infile[10],outfile[10]; printf("输入读入文件的名字:"); scanf("%s",infile); printf("输入输出文件的名字:"); scanf("%s",outfile); if((in=fopen(infile,"w"))==NULL) { printf("无法打开文件\n"); exit(0); }//打开文件1,判断是否有错误 if((out=fopen(outfile,"w"))==NULL) { printf("无法打开文件\n"); exit(0); }//打开文件2,判断是否有错误 while (!feof(in))//feof是检查所指向的是否结束 { ch=fgetc(in); fputc(ch,out); putchar(ch); } putchar(10); fclose(in); fclose(out); return 0; }
这里面函数是feof函数。
他是用来判断所指向的文件是否结束;他是怎么判断一个文件是否结束的呢;
在访问磁盘文件时候,为了知道访问到了第几个字节的时候,系统用文件“文件读写位置标记”来表示访问的位置,当访问下一个字节时候,标记也会到下一个。所以说判断一个文件是否访问完成,可以根据他的访问位置是否在文件的末尾。
feof(in);结束:1;没有结束:0;
10.3.2 怎么向文件读写一个字符串
函数定义:char *fgets (char *str,int n,FILE*fp);指针函数,返回的是地址。 调用函数:fgets(str,n,fp); 定义了一个数组,其中包含n个元素,最后是字符串结束标志“\0”,所以说真正读入文件的字符串共包含n-1个字符。 为什么用char :???返回值是字符型的数据,
函数定义:int fputs(char*str,FILE*fp);定义一个整形函数,返回值是整型 调用函数:fputs("china",fp);括号内第一个位置可以是字符串常量,字符数组名,字符型指针;数组里的\0不输出到文件中。
看一个例子,从键盘读入若干个字符串,对他们按照字母大小的顺序排序,
然后把排序好的字符串送到磁盘的文件中保存
/*从键盘读入若干个字符串,对他们按照字母大小的顺序排序, 然后把排序好的字符串送到磁盘的文件中保存*/ #include <stdio.h> #include <stdlib.h> #include <string.h> int main() { FILE *fp; char str[3][10],temp[10]; int i,j,k,n=3; printf("ENTER strings:\n"); //字符串赋值 for(i=0;i<n;i++) gets(str[i]);//输入一个字符串给数组 同时顺便返回一个函数值;而该函数值就是这个数组的地址;str[i]是第一行的地址。 //开始选择法排序 for(i=0;i<n-1;i++)//循环一次 循环二次 { k=i; for(j=i+1;j<n;j++)//循环两次 循环一次 if(strcmp(str[k],str[j])>0) k=j;//比较字符串,找出最小的那个字符串的标记给K; if(k!=i)//字符串互换 { strcmp(temp,str[i]); strcpy(str[i],str[k]); strcpy(str[k],temp); } } //打开了一个文件 if((fp=fopen("D:\\CC\\wdw.dat","w"))==NULL) { printf("can't open file !\n"); exit(0); } //输出字符串 printf("\nthe new sequence :\n "); for(i=0;i<n;i++) { fputs(str[i],fp); fputs("\n",fp); printf("%s\n",str[i]); } return 0; }
fp=fopen("D:\\CC\\wdw.dat","w") 去指定目录下打开一个文件,写入数据;其实本来因改写成 fp=fopen("D:\CC\wdw.dat","w");这是正常的文件路径 但是\在C语言中包含转义的意思,所以为了避免重复,就用\\代替。
现在说怎么把这个字符串读出。
/*从键盘读入若干个字符串,对他们按照字母大小的顺序排序, 然后把排序好的字符串送到磁盘的文件中保存,现在读出它*/ #include <stdio.h> #include <stdlib.h> #include <string.h> int main() { FILE *fp; char str[3][10]; int i=0; //打开了一个文件 if((fp=fopen("Dstring.dat","r"))==NULL) { printf("can't open file !\n"); exit(0); } //输出字符串 while(fgets(str[i],10,fp)!=NULL) { printf("%s",str[i]); i++; } fclose(fp); return 0; }
本书是谭浩强的书,他在书中使用的是visual c++的环境编译;
我用的是dev c++和sublime txt 无法实现指定路径的写入。这个问题先留着
—————2020-1-1留;
10.3.3 用格式化的方式的读写文件
输入输出函数,如果是以终端为对象进行输入输出;(什么是终端,目前你理解为exe的执行窗口。)
printf();输出函数,从变量输出到终端; scanf();输入函数,从终端输入到变量;
如果是以文件为对象,那么就不能使用以上的函数了;需要改为:
fprintf(文件指针,格式字符串,输出表列); fprintf(fp,"%d,%6.2f",i,f);从变量输出到文件记事本中 fscanf(文件变量,格式字符串,输入表列); fscanf(fp,"%d,%f",&i,&f);从文件记事本中输入到变量中
文件是在磁盘中,变量是在内存里,他们之间只能通过二进制来转化。
输入时要将文件中的ascall码转换成二进制形式再保存在内存变量中,在输出的时候又要将内存中的二进制形式转换成字符。
这样实现了以文件为终端的输入输出,但是耗时比较多。
10.3.4用二进制向文件读写一组数据
那咱们不转化,直接针对二进制文件,对于某种二进制文件往往比较大,也就是说文件的数据比较多。就是将内存的二进制的数据块直接搬运到磁盘。将磁盘的二进制数据块直接搬运到内存中;这种以文件为对象的输入输出函数:
fread(buffer,size,count,fp);从文件读出,保存到内存。 buffer:是一个地址。存放数据的(内存中的)存储区的地址 size:要读写的字节数 count:要读写多少个数据项(每个数据项长度为size) fp:文件的指针 fwrite(buffer,size,count,fp);从内存读出,写入文件 buffer:是一个地址。将(内存中的)存储区的数据输出到文件,内存存储区地址。 size:要读写的字节数 count:要读写多少个数据项(每个数据项长度为size) fp:文件的指针
fread fwrite 函数执行成功 返回count的数值。是个整数。
咱们看一个例子,来看一看怎么使用这个函数:
/*从键盘输入10个学生的有关数据,然后把他们转存到磁盘文件上去。*/ #include <stdio.h> #define SIZE 10 //定义一个结构体数组包含是个学生的数据 struct student_type { char name[10]; int num; int age; char addr[15]; }stud[SIZE]; //将10个同学的数据从内存中读取出来,然后就是输出到磁盘中 void save() { FILE *fp; int i; //打开文件,这个文件在磁盘中 if((fp=fopen("stu.dat","wb"))==NULL)//用“wb”写入文件中 { printf("cannot open file\n"); return ; } //输入到文件中 for(i=0;i<SIZE;i++) if(fwrite(&stud[i],sizeof(struct student_type),1,fp)!=1) printf("file write error\n"); fclose(fp); } //主函数,将数据填入,并且执行 int main() { int i; printf("please erter data of students:\n"); for (int i = 0; i < SIZE; i++) { scanf("%s%d%d%s",stud[i].name,&stud[i].num,&stud[i].age,stud[i].addr); save(); return 0; } }
这是从内存读出,写入文件的案例
那我同样把这段文件的二进制数据从文件中读出,写到内存中。
/*从键盘输入10个学生的有关数据,然后把他们转存到磁盘文件上去。*/ #include <stdio.h> #include <stdlib.h> #define SIZE 10 //定义结构体数组,包含十个数据 struct student_type { char name[10]; int num; int age; char addr[15]; }stud[SIZE]; 定义指针变量,打开的文件指向指针变量,用fread 读写 int main() { int i; FILE *fp; if((fp=fopen("stu.dat","rb"))==NULL) { printf("cannot open file\n"); exit(0) ; } for(i=0;i<SIZE;i++) { fread(&stud[i],sizeof(struct student_type),1,fp); printf("%-10s %4d %4d %-15s",stud[i].name,stud[i].num,stud[i].age,stud[i].addr); } fclose(fp); return 0; } }
注意:(??)
之前说了,在文件与内存之间。文件是在磁盘中,磁盘是在外存中。所以文件的数据换到内存中的时候,其中换行符和回车统统转换成换行符。就是说你代码上写一串换行符号,exe执行文件显示换行,你在exe文件敲打一个回车,同样是换行,就是说,数据从文件到内存,换行符和回车都被转换成换行。
从内存读出的时候,不发生字符转换。他也没有回车,只有换行符号。
咱们敲打的字写exe文件中的数据是在哪儿。送到内存的缓冲区。就相当于输入数据到内存。这是手动敲打。从文件读入也是输入数据的一种方式。都是一样的。都属于输送数据。
总结
就是输送数据:
1.exe上直接敲;手动输入数据
2.直接读文件数据;自动读入数据。
他们输入到内存都要转换。因为只有换行符号有对应的二进制代码。
这样理解可能是错的,读者这段独白属于在目前知识体系下的理解。仅供参考