1. 文件的概念(为何在编程中要使用文件)
提起文件,我们大家都不陌生,有纸质文件、电子文件等,为何要有文件呢?例如法律文件,当出现冲突双方意见不一致时,各执一词,此时就需要查阅相应的法律文件,找到对应的条款,依据条款来进行处理。所以,文件是将数据进行记录,以便以后方便查阅。每次上课的内容我都记录在word文档中,每一个word文档也是一个文件,是电子文件。那么,为何在编程中要使用文件呢?
我们之前设计了不少的程序,例如:求10个整数中的最大值。有的同学在宿舍调试好了,调试的时候每运行一次程序,她都要从键盘输入这10个整数,假如是100个整数呢?是不是有些小崩溃。学完结构体之后,需要处理的数据更多了,程序不是一次两次就可以调试成功的,有时候费了半天劲输入数据之后,啥结果也没有。。。内心欲哭无泪吧。
能不能把我们需要处理的数据也保存在文件中,需要的时候从文件中进行获取,就像硬盘上的文件,只要硬盘不坏,它一直都在。这样的话,输入大量的数据这件事也是比较开心的啦,只需一次输入即可。
那么,如何把数据保存在文件中?如何去文件中进行数据的读取呢?假如程序对某些数据进行了修改,修改后的数据如何存储在文件中呢?不要慌,一系列文件操作函数就可以轻松地来处理这些事情。
2. 对文件进行读写操作的两个难缠思维
(1)读文件。
同学们,当你看(也即读)一份文件的时候,你是如何进行操作的呢?例如,我们的课本也是一份文件(纸质文件),当你想看(也即读)指针这一章内容的时候,你如何操作?打开、找到指针这一章、读上面的内容(把上面的内容存到了你的大脑内存中)、合上课本。是不是这样的??这是读文件的过程。另外,大家需要注意的是,我们读文件的时候,这个文件是必须事先存在的。如果不存在,那是读不成的。
如果我们读电子文件例如word文档时,打开该文档(找到要读的位置,默认是文件开头)、进行读操作(把文件的内容存入计算机内存)、关闭文档。
(2)写文件。
如果我们要写一份纸质文件,得先找到写的介质(例如一个本子),然后打开本子(找到要写的位置:可以从头写,可以找任意位置写),进行写操作(把我们大脑内存中的内容写入本子上)、合上本子结束写操作。
如果我们要写一份电子文件,得先创建文件(创建文件也即让操作系统分配空间,分配空间成功后才能进行下一步操作),然后打开这个文件(找到要写的位置:可以从头写,可以找任意位置写),进行写操作(把电脑内存中的内容写入文件)、关闭文件结束写操作。针对第二个写操作(把电脑内存中的内容写入文件)有的同学可能会说,老师,我直接创建一个word,我把我大脑里面的内容直接写到了word中,和电脑内存有啥关系?其实,操作系统把我们输入的内容暂存在内存中,然后又存入了文件中。这一切我们感受不到而已,只要我们使用计算机,所做的一切都离不开CPU的处理,所处理的内容都离不开内存的存储。
3. 通过实例演示文件操作
3.1 写入字符串、读取字符串:函数fgets和fputs
3.1.1打开文件、写入字符串、关闭文件
#include<stdio.h>
#include<stdlib.h>
int main()
{
FILE *fp;
fp=fopen("e:\\lcy\\a.txt","w");
if(fp==NULL)
{
printf("failed!\n");
exit(0);
}
fputs("hello,big data!\n",fp);
fclose(fp);
return 0;
}
说明:
(1)对文件的操作必须依靠文件指针来完成,FILE *fp;定义了一个文件指针fp,fp是一个指向FILE类型的指针变量。
(2)fopen函数完成了对文件的打开操作,fopen("e:\\lcy\\a.txt", "w");将e:\lcy\a.txt文件打来,w是以写方式打开,说明要对该文件进行写操作,也即需要把内存中的数据存入该文件中。fp=fopen("e:\\lcy\\a.txt", "w");以写的方式打开该文件,并将该文件的地址赋值给fp,也即fp指向了该文件。
(3)fputs函数将字符串写入指定文件,fputs("hello,big data!\n",fp);将"hello,big data!\n"写入fp所指向的文件中。
前面我们学过puts函数是将内存中的字符串输出到显示器,fputs是将内存中的字符串输出到文件中。
(4)fclose函数是关闭文件。fclose(fp);将fp所指向的文件关闭。
3.1.2打开文件、读字符串、关闭文件
#include<stdio.h>
#include<stdlib.h>
int main()
{
FILE *fp;
fp=fopen("e:\\lcy\\a.txt","r");
if(fp==NULL)
{
printf("failed!\n");
exit(0);
}
char s[255];
fgets(s,sizeof(s),fp);
puts(s);
fclose(fp);
return 0;
}
说明:
(1)fp=fopen("e:\\lcy\\a.txt","r"); r说明以写方式打开该文件,并将该文件的首地址赋值给fp,也即fp指向了该文件。
(2)fgets函数用于从文件中读取一行字符串,或者读取指定长度的字符串,fgets(s,sizeof(s),fp);从fp所指向的文件中,读取siziof(s)长度的字符串存入数组s中。
前面我们学过gets,用于从键盘输入一个字符串到内存中,fgets函数是从文件中读取一个字符串到内存中。
3.1.3写入多行字符串、读取多行字符串
对文件执行写操作
#include<stdio.h>
#include<stdlib.h>
int main()
{
FILE *fp;
fp=fopen("e:\\lcy\\a.txt","w");
if(fp==NULL)
{
printf("failed!\n");
exit(0);
}
char* s[3]={"hello\n","students\n","big data!\n"};
int i;
for(i=0;i<3;i++)
fputs(s[i],fp);
fclose(fp);
return 0;
}
对文件执行读操作
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
FILE *fp;
fp=fopen("e:\\lcy\\a.txt","r");
if(fp==NULL)
{
printf("failed!\n");
exit(0);
}
char s[100];
while(!feof(fp))
{
memset(s,0,sizeof(s));
fgets(s,sizeof(s),fp);
puts(s);
}
fclose(fp);
return 0;
}
说明:函数feof用来判断文件是否结束,如果文件结束,该函数返回值为1,否则为0。函数memset(s,0,sizeof(s));用来把s的前sizeof(s)个字节设置为0。
3.2 单字符读写文件:函数fputc和fgetc
3.2.1 fputc函数用于向文件写入字符
#include<stdio.h>
#include<stdlib.h>
int main()
{
FILE *fp;
fp=fopen("e:\\lcy\\a.txt","w");
if(fp==NULL)
{
printf("failed!\n");
exit(0);
}
char s[100]="I like C Program";
int i;
while(s[i]!='\0')
{
fputc(s[i],fp);
i++;
}
fclose(fp);
return 0;
}
说明:函数fputc(s[i],fp);
用于将s[i]中的数据写入fp所指向的文件中。
3.2.2 fgetc函数用于从文件读出字符
#include<stdio.h>
#include<stdlib.h>
int main()
{
FILE *fp;
fp=fopen("e:\\lcy\\a.txt","r");
if(fp==NULL)
{
printf("failed!\n");
exit(0);
}
char ch;
ch=fgetc(fp);
while(ch!=EOF)
{
putchar(ch);
ch=fgetc(fp);
}
fclose(fp);
return 0;
}
说明:函数fgetc(fp)用于从fp所指向的文件中读取一个字符。如果遇到文件结束符,该函数返回EOF。可以利用该函数来判断文件中的数据是否被读完。
ch=fgetc(fp);
while(ch!=EOF)
{
putchar(ch);
ch=fgetc(fp);
}
这段代码可以被改写为:
while((ch=fgetc(fp))!=EOF)
putchar(ch);
3.3 文件格式输入、输出函数:fprintf和fscanf
函数printf和scanf可以设置格式,那么,从文件中读取或写入数据时,同样可控制数据的格式。
3.3.1函数fprintf将数据以一定格式输出到文件中
函数调用形式:
fprintf(文件指针变量,格式控制字符串,输出项列表);
含义:将输出项列表中的各项按照格式控制字符串中的指定格式输出到文件中。
一个无聊的例子:
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
int main()
{
FILE *fp;
fp=fopen("e:\\lcy\\a.txt","w");
if(fp==NULL)
{
printf("failed!\n");
exit(0);
}
char ch;
int x;
double y;
scanf("%c%d%lf",&ch,&x,&y);
fprintf(fp,"%4c%10d%7.2lf",ch,x,y);
fclose(fp);
return 0;
}
说明:函数fprintf(fp,"%4c%10d%7.2lf",ch,x,y);是将ch,x和y以%4c%10d%7.2lf
这种格式输出到fp所指的文件中。
3.3.2函数fscanf是将数据以一定格式从文件中读取出来
函数调用形式:
fscanf(文件指针变量,格式控制字符串,变量地址列表);
含义:将文件中的数据按照格式控制字符串中的指定格式读出来,然后分别赋值给指定变量。
一个更无聊的例子:
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
int main()
{
FILE *fp;
fp=fopen("e:\\lcy\\a.txt","r");
if(fp==NULL)
{
printf("failed!\n");
exit(0);
}
char ch;
int x;
double y;
fscanf(fp,"%c%d%lf",&ch,&x,&y);
printf("%c\n%d\n%lf\n",ch,x,y);
fclose(fp);
return 0;
}
说明:函数fscanf(fp,"%c%d%lf",&ch,&x,&y);
是从fp所指的文件中按照%c%d%lf
这种格式进行读取,并将读取后的数据赋给相应的变量。
3.4 批量读取或者块操作函数:fwrite和fread
批量操作可以将一批数据(如数组所有元素值、结构体变量的值)作为一个整体一次性从文件中进行读或写。
3.4.1函数fwrite对文件执行成块写入操作
函数调用形式:fwrite(buffer,size,count,fp);
含义:从buffer所代表的地址开始,每次输出字节大小为size的数据块,一共输出count,输出的内容存入fp所指的文件中。
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
struct stud
{
char XH[10];
char name[12];
float score;
};
int main()
{
FILE *fp;
fp=fopen("e:\\lcy\\a.txt","wb");
if(fp==NULL)
{
printf("failed!\n");
exit(0);
}
struct stud s;
char c[4],ch;
do
{
gets(s.XH);
gets(s.name);
gets(c);
s.score=atof(c);
fwrite(&s,sizeof(s),1,fp);
printf("input continue(y/n)?");
ch=getchar();getchar();
}while(ch=='y'||ch=='Y');
fclose(fp);
return 0;
}
说明:运行完该程序之后,我们打开e:\lcy\a.txt,发现该文件中的内容成了乱码,不像以前,我们可以看见文件中的内容。这是因为,以前的文件是文本文件,而这次的文件是二进制文件。因为fwrite和fread是按块操作,必须采用二进制形式。那么,如何看该文件中的内容呢,可以把它读出来,显示在屏幕上。
3.4.2函数fread对文件执行成块读取操作
函数调用形式:fread(buffer,size,count,fp);
含义:从fp所指的文件中,每次读取字节大小为size的数据块,一共读取count次,读取的内容存放在以buffer为起始地址的内存单元中。若函数执行成功则返回count的值。
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
struct stud
{
char XH[10];
char name[12];
float score;
};
int main()
{
FILE *fp;
fp=fopen("e:\\lcy\\a.txt","rb");
if(fp==NULL)
{
printf("failed!\n");
exit(0);
}
struct stud s;
printf("xh name score\n");
while(fread(&s,sizeof(s),1,fp)==1)
{
printf("%s %s %f\n",s.XH,s.name,s.score);
}
fclose(fp);
return 0;
}
4. 其它内容
前面我们所讲的对文件进行读写操作,均是从文件的开始文件进行的,其实可以指定读写的位置。函数fseek的作用就是将文件位置指针指向特定位置。
该部分内容感兴趣的同学麻烦自学吧,不再增加大家的负担了。美吧。