文件可以理解为有打开、读、写、关闭等操作的一类对象的抽象,如输入、输出设备、磁盘区块等。磁盘文件是按照某个规则集合在一起保存在磁盘上的一批数据。磁盘文件即可以用于程序的数据输入,也可以用于数据的输出。
1 磁盘文件存储位置的标识
磁盘文件存在在磁盘的某一个区块上,以唯一的路径+文件名+扩展名来标识一个区块地址。
路径可以是绝对路径,也可以是相对路径,如果是相对路径,表示磁盘文件与程序源文件处于相同路径下。
2 文件的类型
按文件中数据组织形式,可以把文件分为文本文件和二进制文件。
2.1 文本文件
在文本文件中,存放的数据都是将其转换成对应的ASCII代码字符来存放的,该文件由一个个字符组成,每一个字节存放一个ASCII码值,代表一个字符。例如,一个整型数据-15621在文本文件中按字符存放,分别存放字符‘-’、‘1’、‘5’、‘6’、‘2’、‘1’,共占6个字节;一个单精度类型数据3.14159,分别存放的是字符‘3’、‘.’、‘1’、‘4’、‘1’、‘5’、‘9’,共占7个字节。
2.2 二进制文件
二进制文件中的数据都是按其二进制方式存放的,每个数据占用的字节数取决于该数据的数据类型。例如,一个整型数据-15621在二进制文件中占4个字节,单精度类型数据3.14159在二进制文件中占4个字节。
3 文件信息标识
ANSI C对两类文件采取缓冲文件系统(程序处理数据只能在内在中而不能直接在磁盘上进行操作。),系统自动在内在中为每个正在使用的文件开辟一个缓冲区,文件的存取通过缓冲区进行。
对缓冲区的相关信息的抽象用一个结构体来描述(如缓冲区与磁盘文件的交互的文件名、状态,缓冲区位置、当前操作位置等),该结构体由系统定义,一般取名为“FILE”,定义在stdio.h文件中(磁盘文件可用于输入、输出)。
在系统中提供了各种处理文件的函数,如文件打开函数fopen(),此函数即可以返回一个指向FILE结构体的指针。每打开一个文件即在系统内开辟一个缓冲区及一个保存FILE的内在单元。
4 文件的打开
在C语言之中,对文件读写之前必须先打开文件。
文件打开函数fopen的调用格式为:
FILE *fp;
fp=fopen(filename,mode);
文件打开函数fopen如果打开成功,就返回一个FILE的指针,并赋值给文件指针变量fp,如果失败,则返回NULL。
文件打开函数fopen有两个参数,filename指定打开的文件名,mode指定文件打开的方式。
mode模式:
文本文件
二进制
读
写
如果文件不存在
备注
r
rb
read
报错
w
wb
write
新建
a
ab
append
新建
r+
rb+
read
+write
报错
w+
wb+
+read
write
新建
a+
ab+
+read
append
新建
备注:
1 b表示binary,表示读写二进制文件;
2 read是读已经存在的数据,所以要求文件存在;
3 write是写在空白文件上,所以文件不存在时新建即可;
4 append是一种特殊的写,所以文件不存在也新建即可;
5 “+”表示附加另一种操作,是r就附加w,是w或a就附加r。是否要求文件存在以“+”前的读写方式决定;
5 文件关闭
使用完一个文件后应该去关闭它,以免它再被误用,造成数据丢失(文件数据操作在缓冲区进行,缓冲区的数据在文件关闭后会写入到磁盘文件中。当你往文件写入数据时,如果你忘记关闭操作,你打开磁盘文件查看时会发现数据并未写入,原因可能就是未关闭文件)。所谓“关闭”就是使文件指针变量不指向该文件,以后不能再通过该指针对其相连的文件进行读写操作。同时,将缓冲区的数据写入到磁盘文件中。如果需要进行读写操作,要再次打开该文件。
文件关闭函数fclose的调用格式为:
fclose(文件指针);
6 文件的读写操作
当文件以合适的方式打开以后,就可以对其进行读写操作。C语言提供了丰富的数据读写函数,可以按字符读写,可以按行读写,也可以按指定长度的数据块进行读写,还可以进行格式化读写。这些函数都包含在头文件stdio.h中。
文件读写函数会更新FILE的当前位置指针及状态成员。可以通过读写这个位置指针来随机读写文件。
6.1 字符读写
字符读写函数在处理文件中的数据时,是以字符为单位进行读写的,即每次只读写一个字符。它常用来处理文本文件,但也可以处理二进制文件。
6.1.1 读取字符函数fgetc
读取字符函数fgetc的调用格式为:
fgetc(文件指针);
6.1.2 写入字符函数fputc
向文件中写入字符函数fputc的调用格式为:
fputc(字符,文件指针变量);
例:
include
void main(int argc,char argv[]) //命令行参数
{
int ch;
FILE in,*out; //定义in和out两个文件类型指针
if(argc!=3) //判断命令行是否正确
{
printf("命令格式错误,正确使用方法: copyfile 文件1 文件2\n");
return;
}
//按读方式打开由argv[1]指出的文件
if((in=fopen(argv[1],"r"))==NULL)
{
printf("文件 %s 打开错误!\n",argv[1]);
return;
}
//按写方式打开由argv[2]指出的文件
if((out=fopen(argv[2],"w"))==NULL)
{
printf("文件 %s 打开错误!\n",argv[2]);
return;
}
//成功打开了argv[2]所指文件
ch=fgetc(in); //从in所指文件的当前指针位置读取一个字符
while(ch!=EOF) //判断刚读取的字符是否是文件结束符
{
fputc(ch,out); //若不是结束符,将它写入out所指文件
ch=fgetc(in); //继续从in所指文件中读取下一个字符
}
fclose(in); //关闭in所指文件
fclose(out); //关闭out所指文件
}
6.2 字符串读写
字符串读写函数是将文件中的数据以字符串为单位进行处理的,即每次一个字符串。字符串读写函数所处理的文件是文本文件,但也可以是二进制文件。
6.2.1 读取字符串函数fgets
从文件中读取字符串函数fgets的功能是从指定的文件中读一个字符串到字符数组中,函数调用的形式为:
fgets(字符数组名,n,文件指针);
fgets(str,n,fp);
6.2.2 写入字符串函数fputs
文件写入字符串函数fputs的功能是把一个字符串写入到指定的文件中。其调用形式为:
fputs(字符串,文件指针)
fputs(“abcd”,fp);
文件读写涉及到两块内存,一块是文件内容的缓冲区,一块是存储文件相关信息的结构体FILE,当写结束时,会在缓冲区的数据末尾附加一个EOF标志,用于读时检测是否到达文件末尾。在读时,当读到这一个EOF标志时,会在FILE改写状态成员的EOF indicator,用于feof()检测。相关细节详见:C++|深入理解文件结束标志EOF
当正确地读或写一个字符串后,FILE中指针当前位置的指针会自动后移一个相应长度的位置。相关的状态信息也会更新到FILE的状态成员中。
6.3 数据块读写
6.3.1 读取数据块的函数fread()
fread()函数用来从指定文件中读一个数据块,该函数的调用格式为:
fread(buffer,size,count,fp);
例:
include
include
struct student
{
char sname[8]; //学生姓名
char ssex[2]; //学生性别
int sage; //学生年龄
}stu1[4];
void main()
{
int i;
FILE *fp;
fp=fopen("student.dat","wb"); //以只写的方式打开二进制文件
if(fp==NULL)
{
printf ("Can not open file!\n") ;
exit (0);
}
printf("四个学生的基本信息:\n");
for(i=0;i<4;i++)
{
scanf("%s%s%d",stu1[i].sname,stu1[i].ssex,&stu1[i].sage); //读取学生的信息
fwrite(&stu1[i],sizeof(struct student),1,fp); //写到文件中
}
fclose(fp);
}
6.3.2 写入数据块的函数fwrite
fwrite函数用来将一个数据块写入文件,该函数的调用格式为:
fwrite(buffer,size,count,fp);
例:
include
include
struct student
{
char sname[8]; //学生姓名
char ssex[2]; //生性别
int sage; //生年龄
}stu[4];
void main()
{
FILE *fp;
int i;
fp=fopen("student.dat","rb"); //以只读方式打开二进制文件
if(fp==NULL)
{
printf("file can not open!");
exit(0);
}
printf("sname--ssex--sage\n");
for(i=0;i<4;i++)
{
fread(&stu[i],sizeof(student),1,fp); //fp所向的文件中读取一个学生信息
printf("%s-%s-%d",stu[i].sname,stu[i].ssex,stu[i].sage);
printf("\n");
}
fclose(fp); //关闭文件
}
6.4 格式数据读写
格式数据读写函数fscanf()、fprintf()与格式输入输出函数scanf()、printf()类似,是以格式来控制读写的数据,可以是字符型、整型、实数型等数据。
6.4.1 格式数据读取函数fscanf
格式数据读取函数fscanf类似与scanf函数,两者都是格式化输入函数,不同的是scanf函数的作用对象是终端键盘,而fscanf函数的作用对象是文件。fscanf函数调用的一般格式为:
fscanf(文件指针,格式控制,输入列表);
6.4.2 格式数据写入函数fprintf
格式数据写入函数fprintf类似于格式输出函数printf,两者都是格式化输出函数,只不过两者的作用对象不同,fprintf函数输出到文件,printf函数输出到终端。fprintf函数调用的一般格式为:
fprintf(文件指针,格式控制,输入列表);
例:
include
include
void main( )
{
FILE fp1,fp2;
char ch;
fp1=fopen("test.txt","r"); //以只读方式打开文本文件test.txt
if(fp1==NULL)
{
printf ("Can not open file!\n") ;
exit (0);
}
fp2=fopen("test2.txt","w"); //以只写方式打开文本文件test2.txt
if(fp2==NULL)
{
printf ("Can not open file!\n") ;
exit (1);
}
fscanf(fp1,"%c",&ch); //从文件test.txt中读取字符
while(!feof(fp1)) //判断是否到文件尾
{
if(ch>='A'&&ch<='Z'||ch>='a'&&ch<='z') //找出文件中的英语字符
fprintf(fp2,"%c",ch); //将从文件test.txt中读取的字符写入test2.txt中
fscanf(fp1,"%c",&ch); //从文件test.txt中读取字符
}
fclose(fp1);
fclose(fp2);
}
这些文件读写函数是状态函数,涉及到两块内存单元,缓冲区和FILE。
输入输出使用缓冲区,抽象为流的概念。一个先进先出的线性结构。输入(文件读)操作,向流中添加数据,输出(文件写)操作,从流中提取数据。添加、提取操作其实是通过一个隐式的指针跟踪读、写的位置。
7 文件的定位
FILE有一个指向当前操作位置的指针,在打开文件时,这个指针指向文件头部(以"a"方式打开时,指向文件尾部),在一次读写操作后,该位置指针会自动指向一下个数据的位置。为了能够从文件中直接读取某个数据,系统提供了能读写FILE的当前位置指针的函数。
文件头定位函数rewind的作用是将文件位置指针返回到文件指针变量指向的文件的开头。
函数rewind调用的一般格式为:
rewind(文件指针);
例:
include
include
void main()
{
FILE *fp;
char ch,st[50];
fp=fopen("word.txt","w"); //以只写方式打开文件
if(fp==NULL)
{
printf("Cannot open file!");
exit(1);
}
printf("input a string:\n");
gets(st); //输入字符串
fputs(st,fp);
rewind(fp); //将文件指针指向文件的开头
ch=fgetc(fp); //读取一个字符
while(ch!=EOF)
{
putchar(ch); //将字符输出
ch=fgetc(fp);
}
printf("\n");
fclose(fp); //关闭文件
}
C语言提供了文件随机定位函数fseek能将文件位置指针按需要移动到任意位置,可以实现对文件的随机读取。
文件随机定位函数fseek的一般调用格式为:
fseek(文件指针,位移量,起始位置);
在对文件进行读写时,特别是多次调用随机定义函数fseek以后,文件位置指针的值经常发生变化,很难确定其当前的位置。C语言定义的了测试当前位置的函数ftell。
例:
//代码效果参考:http://www.zidongmutanji.com/zsjx/551475.html
include
include
void main( )
{
FILE *fp;
int i;
char s1[80],s[]="abcdefghijklmnop";
fp=fopen("art.dat","wb+"); //以读写的方式打开二进制文件"art.dat"
if(fp==NULL)
{
printf ("Can not open file!\n") ;
exit (1) ;
}
i=sizeof(s); //求字符串的大小
fwrite(s,i,1,fp); //将字符串写入文件中
rewind(fp); //将文件指针指向文件头
fread(s1,i,1,fp); //读取输入的字符串
printf("input:%s\n",s1);
fseek(fp,0L,0); //文件位置指针移到距文件头位置
printf("seek1 ch=%c\n",fgetc(fp));
fseek(fp,10L,1); //文件位置指针移到距文件当前位置10个字节的位置
printf("seek2 ch=%c\n",fgetc(fp));
fseek(fp,1L,1); //文件位置指针移到距文件当前位置1个字节的位置
printf("seek3 ch=%c\n",fgetc(fp));
fclose(fp);
}
测试当前位置函数ftell调用的一般格式为:
ftell(文件指针);
8 文件检测
读写FILE中文件状态成员可以对文件末尾、读写出错等方面讲行的检查和测试,C语言常用的文件检测函数有文件末尾检测函数feof()、文件读写出错检测函数ferror()等。
在文本文件中,C语言规定EOF为文件结束标志,EOF的值为-1,因为在ASCII码表中没有-1所对应的字符。但在二进制文件中,-1可能为用效数据,就不能用EOF来作为文件结束标志。
C语言专门定义了feof函数作为二进制文件的结束标志,也可以作为文本文件的结束标志。
文件末尾检测函数feof的一般调用格式为:
feof(文件指针);
在程序执行过程中,特别是文件读写过程中,会出现一会不可预见的错误。C语言定义了文件读写出错检测函数ferror。
读写文件出错检测函数ferror函数的一般调用格式:
ferror(文件指针);
在文件打开时,出错标志置为0,一旦文件读写过程出现错误,错误标志被置为非0值,直到同一文件调用clearerr函数或rewind函数才重新置0。
出错标志函数clearer调用的一般格式为:
clearerr(文件指针);
clearerr函数的功能是用于将文件指针变量指向的文件的出错标志和文件结束标志设置为0值。