c语言文件的输入输出
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
C语言文件的输入与输出
一.文件指针
每个被使用的文件都在内存中开辟一个区,用来存放文件的有关信息(如文件的名字、文什状态及文件当前位置等)。
这些信息是保存在一个结构体变量中的。
该结构体类型是由系统定义的,取名为FILE。
在sudio.h文件中有以下的文件类型声明:
typedef struct{
short level; /*缓冲区“满”或“空”的程度*/
unsigned flags; /*文件状态标志*/
char fd; /*文件描述符*/
unsigned char hold; /*如无缓冲区不读取字符*/
short bsize; /*缓冲区的大小*/
unsigned char *buffer;/*数据缓冲区的位置*/
unsigned char *curp; /*指针,当前的指向*/
unsigned istemp; /*临时文件,指示器*/
short token; /*用于有效性检查*/
}FILE;
在缓冲文件系统中,每个被使用的文件都要在内存中开辟一FILE 类型的区,存放文件的有关信息。
FILE类型的数组:FILE f[5];定义了一个结构体数组f,它有5个元素,可以用来存放5个文件的信息。
有了文件指针类型,就可以用它来定义文件指针变量,用文件指针变量来访问文件,其形式为:FILE *指针变量名。
例如:FILE *fp;其中fp是指向要访问文件的指针变量。
例如,通过文件指针访问text.exe文件,有如下的访问:
FILE *fp;
fp=fopen(“text.exe”, “rb”);
ch=getc(fp);
第一行语句只定义了一个没有指向的文件指针fp,第二行是打开文件text.exe,并同时确定对访问文件的操作方式(此处对访问的文件只进行读操作),这时给文件结构中的成员信息赋值,并使fp指向text.exe 文件,第三行是利用文件指针对文件进行操作,操作方式应当与打开文件时确定的方式一致(只读)。
由此可以看出,操作文件过程中不出现对文件结构成员的访问语句。
如果有n个文件,一般应设n个指针变量,使它们分别指向n个文件,确定打开方式,利用有关函数对文件指针操作即可。
例如:下面语句定义了三个文件指针,在程序中就可以处理三个不同的文件。
FILE *fa,*fb,*fc;
二.文件的打开与关闭
2.1文件的打开(fopen函数)
打开文件的首要工作就是要改变文件的标志,使其由闭到开,并且把下面信息告诉编译系统:
①需要打开的文件名,也就是准备访问的文件的名字;
②使用文件的方式(“读”还是“写”等);
③让哪一个指针变量指向被打开的文件。
打开函数的原型是定义在stdio.h头文件中的fopen函数,其格式为: fopen(“文件名”,“使用文件方式”);
即:FILE=fopen(“文件名”,“使用文件方式”);
例如:fp=fopen(“file”,“r”);
它表示,要打开的文件名为file,操作方式为读入,fopen函数返回指向file文件的指针并赋值给指针变量fp,这样fp和文件file就建立了联系。
文件名字符串允许带有路径,使用路径时,路径分隔符是“\” 而不是“\\”。
例如:FILE *fp,*fq;
fp=fopen(“student”,“w”);
fq=fopen(“D:liuli\student34”,“r”);
以上语句表明,在当前磁盘,当前目录下以只写方式打开一个新文件,并将该文件的内存存储的首地址赋予文件指针fp,再以只读方式打开D盘liuli子目录下名字为student34的已经存盘的旧文件,并将该文件的内存存储的首地址赋予文件指针fq。
注意:如果fopen函数调用失败,即文件打开失败,fopen函数将返回空指针NULL,NULL在头文件中被定义为0.
文件使用方式及含义
说明:
(1)用“r”方式打开文件的目的是为了从文件中读取数据,不能向文件写入数据,而且该文件应该已经存在,不能用“r”方式打开一个并不存在的文件;否则出错。
(2)用“w”方式打开的文件只能用于向该文件写数据(即输出文件),而不能用来向计算机输入。
如果原来不存在该文件,则在打开时新建立一个以指定的名字命名的文件。
如果原来已存在一个以该文件名命名的文件,则在打开时将该文件删去,然后重新建立个新文件。
(3)如果希望向文件末尾添加新的数据(不希望删除原有数据),则应该用“a”方式打开。
但此时该文件必须已存在;否则将得到出错信息。
打开时,位置指针移到文件末尾。
(4)用"r+”、“w+”、“a+”方式打开的文件既可以用来输人数据,也可以用来输出数据。
用"r+”方式时该文件应该已经存在,以便能向计算机输人数据。
用“w+”方式则新建立一个文件,先向此文件写数据,然后可以读此文件中的数据。
用“a+”方式打开的文件,原来的文件不被删去,位置指针移到文件末尾,可以添加,也可以读。
(5)如果不能实现“打开”的任务,fopen函数将会带回一个出错信息。
出错的原因可能是用"r"方式打开一个并不存在的文件;磁盘出故障;磁盘已满无法建立新文件等。
此时fopen函数将带回一个空指针值NULL(NULL在stdio.H头文件中已被定义为0)。
常用下面的方法打开一个文件:
if((fp=fopen("file","r"))==NULL)
{ printf("cannot open this file\n");
exit(0);//关闭所有文件,终止正在运行的程序
}
即先检查打开的操作有否出错,如果有错就在终端上输出"can not open this file"。
exit函数的作用是关闭所有文件,终止正在执行的程序,待用户检查出错误,修改后再运行。
(6)从文本文件中读取数据时,将回车符与换行符两个字符转换为一个换行符。
在向文本文件中写入数据时把换行符转换成为回车和换行两个字符。
在用二进制文件时,不进行这种转换,在内存中的数据形式与输出到磁盘中的数据形式完全一致。
2.2、文件的关闭(fclose函数)
在使用完一个文件后应该关闭它,以防止它再被误用。
“关闭”就是使文件指针变量不指向该文件,也就是文件指针变量与文件“脱钩”,此后不能再通过该指针对原来与其相联系的文件进行读写操作,除非再次打开,使该指针变量重新指向该文件。
用fclose函数关闭文件。
fclose函数调用的一般形式为:
fclose(文件指针);
例如:fclosc(fp);
表示关闭由文件指针fp当前指向的文件,收回其占有的内存空间,
取消文件指针fp的指向。
如果在程序中同时打开多个文件,使用完后必须多次调用fclose函数将文件逐一关闭。
关闭成功返回值为0;否则返回EOF(-1) 。
如果不关闭文件将会丢失数据。
因为,在向文件写数据时,是先将数据输出到缓冲区,待缓冲区充满后才正式输出给文件。
如果当数据未充满缓冲区而程序结束运行,就会将缓冲区中的数据丢失。
用fclose函数关闭文件,可以避免这个问题,它先把缓冲区中的数据输出到磁盘文件,然后才释放文件指针变量。
三.文件的读写
1.单个字符的读写操作(fputc()和fgetc())
3.1.1、 fputs函数---字符的写操作
用fputc函数可以把一个字符写到磁盘文件中去,其形式为:
fputc(int ch,FILE *fp)
其中,ch为输出字符,它可以使一个字符常量,也可以使一个字符变量,fp是文件指针变量,它已经指向一个由函数打开的文件。
函数功能: 将字符(ch的值)输出到fp所指向的文件中去。
如果输出成功,则返回值就是输出的字符;如果输出失败,则返回一个EOF.
3.1.2、fgetc函数---字符的读操作
fgetc函数的原型定义在stdio.h头文件中,该函数可以从指定的文件读取一个字符,该文件必须是以读或读写方式打开的。
其格式为:fgetc(FILE *fp);
fp为文件型指针变量,fgetc函数带回一个字符。
如果读到的是文本文件结束符,函数返回—个文件结束标志EOF(即-1)。
如果想从一个文本文件顺序读入字符并在屏幕上显示出来,常见的读取字符操作为:
ch = fgetc(fp);
while(ch!=EOF)
{
putchar(ch);
ch = fgetc(fp);
}
注意:EOF不是可输出字符,因此不能在屏幕上显示。
由于字符的ASCll码不可能小于-1,因此EOF定义为-1是合适的。
当读入的字符值等于-1(即EOF)时,表示读入的已不是正常的字符而是文件结束符。
但以上只适用于读文本文件的情况,并不实用于处理二进制文件,因为读入某一个字节中的二进制数据的值有可能是-l,而这又恰好是EOF的值,这就会出现文件没结束但被判断结束的结果。
为了解决这个问题.采用feof(fp)函数,feof(fp)用来测试fp所指向的文件当前状态是否“文件结束”。
如果是文件结束,函数feof(fp)的值为1(真);否则为0(假)。
如果想顺序读入一个二进制文件中的数据。
可以用从一个二进制文件顺序读入字符:
while(!feof(fp))
{
ch = fgetc(fp);
…………
}
3.1.3、fputc和fgetc函数使用举例
例1:函数fputc的应用
#include <stdlib.h>
#include <stdio.h>
void main(void)
{ FILE *fp; /*定义文件指针*/
char ch,filename[10];
scanf("%s",filename);
if((fp=fopen(filename,"w"))==NULL)
{ printf("cannot open file\n");
exit(0); /*终止程序*/
}
ch=getchar( ); /*接收输入的第一个字符*/
while(ch!=‘#')
{
fputc(ch,fp);
putchar(ch);
ch=getchar();
}
printf("\n"); /*向屏幕输出一个换行符*/
fclose(fp);
}
运行情况如下:
file1.c(输入磁盘文件名)
computer and c# (输入一个字符串)
computer and c (输出一个字符串)
文件名由键盘输入,赋给字符数组filename。
fopen函数中的第一个参数“文件名”可以直接写成字符串常量形式(如file1.c).也可以用字符数组名,在字符数组中存放文件名(如本例所用的方法)。
本例运行时从键盘输人磁盘文件名“filel.c”,然后输入要写入该磁盘文件的字符"computer and c",“#”是表示输入结束。
程序“computer and c”写到以“filel.c”命名的磁盘文件中,同时在屏幕上显示这些字符。
为了验证computer and c”是否写到以“filel.c”命名的磁盘文件中,我们可以对还磁盘中的内容进行读取操作,在在屏幕上显示磁盘中的内容。
,则有
例2. 函数fgetc的应用
#include<stdlib.h>
#include<stdio.h>
void main(void)
{ FILE *fp;/*定义文件指针*/
char ch;
if((fp=fopen("file1.c","r"))==NULL)
{ printf("cannot open file1.c\n");
exit(0); /*终止程序*/
}
ch=fgetc(fp);
while(!feof(fp))
{
putchar(ch);
ch=fgetc(fp);
}
printf("\n"); /*向屏幕输出一个换行符*/
fclose(fp);
}
注:在stdio.h头文件中,用putc和fputc及用getc和fgetc是一样的。
一般可以把它们作为相同的函数来对待。
2.字符串的读写操作
3.2.1、fgets函数
f gets函数原型定义在stdio.h中,指从指定文件读入一个字符串。
其格式为:fgets(char *str,int num,FILE *fp);
其中fp指向被读文件的指针变量, nun-1是读出字符串的字符个数,str是存放读出的字符串的数组名,该函数返回str。
其功能是从指定文件fp中读入若干字符并存入到字符串变量中,当读完num-1个字符或读到一个换行符‘\n’,则结束字符读入。
如果读到换行符结束,此时‘\n’也作为一个字符送入str数组中,同
时,在读入的所有字符之后自动加一个‘\0’,因此送到数组中字符串最多占有num个字节,fgets()函数的返回值为str数组的首地址。
如果读到文件末尾或出错,则返回NULL。
3.2.2、fputs函数
f puts函数原型定义在stdio.h中,指写一个字符串到指定文件。
其格式为:fputs(char *str,FILE *fp);
其中,str是存放写入的字符串的数组名,fp是指向被写入文件的指针变量,该函数的返回值为整型值,操作正确时返回0,操作错误时返回非0值。
3.2.3 fputs和fgets使用举例
例3.#include<stdio.h>
void main()
{
FILE *fpin,*fpout;
char a[10];
fpin=fopen("1.txt","r");
fpout=fopen("2.txt","w");
fgets(a,5,fpin);
fputs(a,fpout);
fclose(fpin);
fclose(fpout);
}
3、数据块读写函数(fread()和fwrite())
3.3.1 fwrite()函数---数据快的写操作
fwrite()函数的原型定义在stdio.h头文件中,功能是将一个数据块写入磁盘中,格式为:
fwrite(void *buffer,int size,int count,FILE *fp);
其中,buffer是指向写入数据块存放空间的指针,size要输入数据块的字节数,count为要写入多少个以size为长度的数据块的个数。
fp是指向被写入文件的指针。
例如,若文件以二进制形式打开:
fwrite(f,4,2,fp);其中f是—个实型数组名。
此函数从数组f中读取2个4个字节的数据块,存储在fp中。
使用举例:若有如下结构类型:
struct stu
{char name[10];
int num;
int age;
char addr[30];
}stud[40];
结构体数组stud有40个元素.每—个元素用来存放一个学生的数据(包括姓名、学号、年龄、地址)。
假设学生的数据已有放在结构体数组stud中.可以用下面的for语句和fwrite函数写入40个学生的数据:
for(i=0;i<40;i++);
fwrite(&stud[i],sizeof(struct stu),1,fp);
3.3.2 fread()函数---数据块的读操作
fread()函数的原型定义在stdio.h头文件中,功能是将磁盘的一个数据块读到内存中,格式为:
fread(void *buffer,int size,int count,FILE *fp);
其中,buffer是指向读入数据块存放空间的指针,size要读入数据块的字节数,count要进行读入多少个以size为长度的数据块的个数。
fp是指向被读入文件的指针。
例如,若文件以二进制形式打开:fread(f,4,2,fp);其中f是—个实型数组名。
此函数从fp所指向的文件中读入2个4个字节的数据块,存储到数组f中。
使用举例:若有如下结构类型:
struct stu
{char name[10];
int num;
int age;
char addr[30];
}stud[40];
结构体数组stud有40个元素.每—个元素用来存放一个学生的数据(包括姓名、学号、年龄、地址)。
假设学生的数据已有放在磁盘文件中.可以用下面的for语句和fread函数读入40个学生的数据: for(i=0;i<40;i++);
fread(&stud[i],sizeof(struct stu),1,fp);
3.3.3数据块读写操作的实例
例4:从键盘输入4个学生的有关数据,然后把它们转存到磁盘文件上去
#include <stdio.h>
#define SIZE 4
struct student_type
{ char name[10];
int num;
int age;
char addr[15];
} stud[SIZE]; /*定义结构*/
void save( )
{ FILE *fp;
int i;
if((fp=fopen("stulist","wb"))==NULL)
{ 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); /*关闭文件*/ }
main()
{ int i;
for(i=0;i<SIZE;i++) /*从键盘读入学生信息*/
scanf("%s%d%d%s",stud[i].name,&stud[i].num,&st ud[i].age,stud[i].addr);
save( ); /*调用save()保存学生信息*/
}
在main函数中,从终端键盘输入4个学生的数据,然后调用save 函数,将这些数据输出到以“slu list"命名的磁盘文件中。
fwfite函数的作用是将一个长度为29字节的数据块送别stulist文件中(一个student type类型结构体变量的长度为它的成员长度之和,即
0+2+2+15=29)。
运行情况如下:
输入4个学生的姓名、学号、年龄和地址:
Zhang 1001 19 room_101
Fun 1002 20 room_102
Tan 1003 21 room_103
Ling 1004 21 room_104
程序运行时,屏幕上并无输出任何信息,只是将从键盘输入的数据送到磁盘文件上。
为了验证在磁盘文件"stulist"中是否已存在此数据,可以用以下程序从“stulist”文件中读入数据,然后在屏幕上输出。
例5.#include <stdio.h>
#define SIZE 4
struct student_type
{char name[10];
int num;
int age;
char addr[15];
}stud[SIZE];
main( )
{int i;
FILE*fp;
fp=fopen("stulist","rb");
for(i=0;i<SIZE;i++)
{fread(&stud[i],sizeof(struct student_type),1,fp);
printf("%\-10s %4d %4d %\-15s\n",stud[i].name, stud[i].num, stud[i]. age,stud[i].addr); }
fclose (fp);
}
屏幕上显示出以下信息:
Zhang 1001 19 room_101
Fun 1002 20 room_102
Tan 1003 21 room_103
Ling 1004 21 room_104
4、格式化读写函数(fprintf()和fscanf())
printf函数、fscanf函数与print函数、scanf函数作用相仿,都是格式化读写函数。
只有一点不同:fprintf和fscanf函数的读写对象不是终端而是磁盘文件。
3.4.1 fscanf()函数---格式化读操作
fscanf的格式为:fscanf(FILE *fp,char *format,arg-list);
其中,fp是指向被读入文件的指针变量,format是指向格式化字符串的指针变量,arg-list是参数表。
例如:将fp指向的文件的数据送入到i和t中,语句如下:
fscanf(fp,“%d,%f”,i,t);
如果fp中有整型量和实型量,则整型送入i中,实型送入到t中,如果有多个整型和实型量,读入哪个数据将由文件位置指针确定。
3.4.2 fprintf()函数---格式化写操作
fprintf的格式为:int fprintf(FILE *fp,char *format,arg-list); 其中,fp是指向被写文件的指针变量,format是指向格式化字符串的指针变量,arg-list是参数表。
例如:将变量i和t的数据送入到fp所指向的文件中,语句如下:fscanf(fp,“%d,%f”,i,t);
3.4.3 fprintf()和fscanf()函数的应用举例
例6.#include<stdio.h>
main()
{
FILE *fp;
int k,n,a[6]={1,2,3,4,5,6};
fp=fopen("d2.txt","w");
fprintf(fp,"%d%d%d\n%d%d%d",a[0],a[1],a[2],a[3],a[4],a[5 ]); //这个输出到文件的格式为123
456
fclose(fp);
fp=fopen("d2.txt","r");
fscanf(fp,"%d%d",&k,&n);
printf("%d,%d\n",k,n);
fclose(fp);
}
例7
#include<stdio.h>
main()
{ FILE *fp;int i,k,n;
fp=fopen("3.txt","w+");
for (i=1;i<=6;i++)
{
fprintf(fp,"%d ",i); ///这里%d后有空格,输出到文件中的数据是1 2 3 (第一行),fscanf读取时是以空格为分界的,所以只能读进来1,2
if(i%3==0) fprintf(fp,"\n");
}
rewind(fp); //后面将介绍此函数
fscanf(fp,"%d%d",&k,&n);printf("%d,%d\n",k,n);
fclose(fp);
}
四.文件的定位
文件中有一个位置指针,指向当前读写的位置。
如果顺序读写一个文件,每次读写一个字符,则读写完一个字符后,该位置指针自动移动指向下一个字符位置。
如果想改变这样的规律,强制使位置指针指向其他指定的位置,可以用后面介绍的有关函数。
顺序读写和随机读写:
顺序读写:位置指针按字节位置顺序移动。
随机读写:读写完上一个字符(字节)后,并不一定要读写其后续的字符(字节),而可以读些文件中任意位置上所需要的字符(字节)。
4.1、rewind函数
rcwind()函数的原型定义在stdio.h头文件中,rcwind()函数的作用是使位置指引重新返回文件的开头。
其格式如下:rewind(FILE *fp);
其中fp是指向文件的指针,该函数操作成功返回0,否则返回其他值。
例8. 有一个磁盘文件,第一次将它的内容显示在屏幕上.第二次把它复制到另一文件上。
#include<stdio.h>
void main()
{ FILE *fp1,*fp2;
fp1=fopen("file1.txt","r");
fp2=fopen("file2.txt","w");
while(!feof(fp1)) putchar(getc(fp1));
rewind(fp1);
while(!feof(fp1))
putc(getc(fp1),fp2);
printf("\n");
fclose(fp1);fclose(fp2);
}
在第一次将文件的内容显示在屏幕以后,文件file11.txt的位置指针已指到文件末尾,feof的值为非零(真)。
执行rewind函数,使文件的位置指针重新定位于文件开头,并使feof函数的值恢复为0(假)。
下面的代码验证file1.txt中的内容完全复制到file2.txt中
例9.#include<stdlib.h>
#include<stdio.h>
void main(void)
{ FILE *fp;/*定义文件指针*/
char ch;
if((fp=fopen("file2.txt","r"))==NULL) { printf("cannot open file2.c\n"); exit(0); /*终止程序*/
}
ch=fgetc(fp);
while(!feof(fp))
{
putchar(ch);
ch=fgetc(fp);
}
printf("\n"); /*向屏幕输出一个换行符*/
fclose(fp);
}
4.2、fseek函数和随机读写(一般用于二进制文件)
fseek()函数的原型定义在stdio.h中,功能是将文件位置指针定位在文件的某字节位置上,其格式如下:
fseek(FILE *fp,long offset,int origin);
其中,fp是指向被操作数文件的指针,offset是移动位置指针的位移量,使用长整型数据,origin是移动位置指针的起始点代号,其中,起始点与代号的关系如下:
位移量是以起点为基点,向前移动的字节数,当位移量为负时,由起始点向后移。
本函数一般用于对二进制文件的操作,因为文本文件要发生字符转换,计算位置容易发生错误。
fseek(fp,100L,0); 将位置指针移到离文件头100个字节处。
fseek(fp,-50L,1);将位置指针向后移动,移到离当前位置50
个字节处。
fseek(fp,50L,2);将位置指针从文件末尾处向后退50个字节。
例10.#include<stdio.h>
#include<iomanip>
void main()
{
FILE *fp;
char ch;
if((fp=fopen("test.txt","rb"))==NULL)
{
printf("cannot open file\n");
exit(1);
}
fseek(fp,15,0);
ch=getc(fp);
putchar(ch);
printf("\n");
fclose(fp);
}
4.3、ftell函数
ftell()函数的原型定义在stdio.h头文件中,功能是测得位置指针当前所指的位置,其格式为:long ftell(FILE *fp);
其中,fp是指向被测文件的指针,ftell()函数返回的是位置指针的地址。
如果出错,将返回-1.如:
i = ftell(fp);
if(i==-1) printf(“error\n”);
变量i存放当前位置,如调用函数时出错(如不存在如文件),则输出“error"。
例11.
#include <stdio.h>
void main()
{
FILE *fp;
fp = fopen("test.txt", "w+");
/*按照格式要求将字符串写入文件*/
fprintf(fp, "This is a test");
/*读出文件指针fp的位置*/
printf("The file pointer is at byte %ld\n", ftell(fp));
fclose(fp);
}
注意:本题中ftell函数的返回值实际上就是该文件的长度。
在实际的应用中,函数ftell常用来计算文件的长度。
本例中首先以写方式打开名为test.txt的文件,按照格式要求将字符串写入文件。
注意:字符串共14个字符,地址为0~13。
调用fprintf函数后,文件指针自动移到读入的最后一个字符的下一个位置,本例中就是文件的结束符,它的地址是14。
五.出错的检测
5.1、ferror函数
在调用各种输入输出函数(如fputc、fgetc、fread、fwrite等)
时,如果出现错误,除了函数返回值有所反映外,还可以用ferror函数检查。
其调用形式:ferror(FILE,*fp);若果函数返回0,表示未出错;返回非0,表示出错。
在调用一个输入输出函数后立即检查ferror函数的值,否则信息会丢失。
在执行fopen函数时,ferror函数的初始值自动置为0。
如:if(ferror(fp))
{ printf(“file can not open\n”);
fclose(fp);
exit(0);
}
5.2、clearerr函数
其调用形式:void clearerr(FILE *fp);
其作用是使文件指针fp指向的文件错误标志和文件结束标志置一起复位为0。
注:若对文件读写时出现了错误,feeror函数就返回一个非0值,而且该值一直保持到文件执行下一次读写时为止。
若及时调用clearerr 函数就能清除出错标志,使feeror的函数值为0
5.3、 exit函数
当文件出现错误时,为了避免数据丢失,正常返回操作系统,可以调用过程控制函数exit()关闭文件,终止程序的执行。
一般形式为:
exit([status]);参数status为状态值,它被传递到调用函数。
status 取0时,表示程序正常执行。
31。