C语言桂林理工大学11第十一章 文件操作.docx
《C语言桂林理工大学11第十一章 文件操作.docx》由会员分享,可在线阅读,更多相关《C语言桂林理工大学11第十一章 文件操作.docx(21页珍藏版)》请在冰点文库上搜索。
C语言桂林理工大学11第十一章文件操作
学时安排:
2~4
授课题目:
第十一章文件操作
目标要求:
掌握文件类型指针的使用;熟悉文件操作函数的功能和使用;了解文件的概念
授课类型:
理论课授课场地:
教室
教学方式:
讲授、演示、提问、讨论教辅设备:
投影仪
授课内容:
第十一章文件操作
11.1文件概述
1、文件:
存储在外部介质上一组相关数据的集合。
例如,程序文件就是程序代码的集合;数据文件是数据的集合。
2、文件名:
操作系统以文件为单位对数据进行管理,每个文件有一个名称,文件名是文件的标识,操作系统通过文件名访问文件。
例如,通过文件名查找,打开文件,然后读取或写入数据。
3、磁盘文件、设备文件:
(1)磁盘文件:
文件一般保存在磁介质(如软盘、硬盘)上,所以称为磁盘文件。
(2)设备文件:
操作系统还经常将与主机相连接的I/O设备(如键盘-输入文件、显示器、打印机-输出文件)也看作为文件,即设备文件。
很多磁盘文件的概念、操作,对设备文件也同样有意义,有效。
4、ASCII文件、二进制文件:
根据文件的组织形式,文件可以分为ASCII文件和二进制文件。
(1)ASCII文件(文本文件):
每个字节存放一个ASCII码,代表一个字符。
ASCII文件可以阅读,可以打印,但是它与内存数据交换时需要转换。
(2)二进制文件:
将内存中的数据按照其在内存中的存储形式原样输出、并保存在文件中。
二进制文件占用空间少,内存数据和磁盘数据交换时无须转换,但是二进制文件不可阅读、打印。
例如:
同样的整数10000,如果保存在文本文件中,就可以用notepad,edit等文本编辑器阅读,也可以在dos下用type显示,它占用5个字节;如果保存在二进制文件中,不能阅读,但是我们知道一个整数在内存中用补码表示并占用2个字节,所以如果保存在二进制文件中就占用2个字节。
文本文件/二进制文件不是用后缀来确定的,而是以内容来确定的,但是文件后缀往往隐含其类别,如*.txt代表文本文件,*.doc,*.bmp,*.exe二进制文件。
5、缓冲文件系统、非缓冲文件系统:
(1)缓冲文件系统:
系统自动地在内存中为每个正在使用的文件开辟一个缓冲区。
在从磁盘读数据时,一次从磁盘文件将一些数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个将数据送给接受变量;向磁盘文件输出数据时,先将数据送到内存缓冲区,装满缓冲区后才一起输出到磁盘。
减少对磁盘的实际访问(读/写)次数。
ANSIC只采用缓冲文件系统。
(2)非缓冲文件系统:
不由系统自动设置缓冲区,而由用户根据需要设置。
C语言中,没有输入输出语句,对文件的读写都是用库函数实现的。
11.2文件类型
1、文件类型(结构体)-FILE类型
FILE类型是一种结构体类型,在stdio.h中定义,用于存放文件的当前的有关信息。
程序使用一个文件,系统就为此文件开辟一个FILE类型变量。
程序使用几个文件,系统就开辟几个FILE类型变量,存放各个文件的相关信息。
typedefstruct{
shortlevel;/*fill/emptylevelofbuffer*/
unsignedflags;/*文件状态标志*/
charfd;/*文件描述符*/
unsignedcharhold;/*Ungetccharifnobuffer*/
shortbsize;/*缓冲区大小*/
unsignedchar*buffer;/*数据传输缓冲区*/
unsignedchar*curp;/*当前激活指针*/
unsignedistemp;/*临时文件指示器*/
shorttoken;/*用于合法性校合*/
}FILE;
2、文件指针变量(文件指针)
通常对FILE结构体的访问是通过FILE类型指针变量(简称:
文件指针)完成,文件指针变量指向文件类型变量,简单地说,文件指针指向文件。
事实上只需要使用文件指针完成文件的操作,根本不必关心文件类型变量的内容。
在打开一个文件后,系统开辟一个文件变量并返回此文件的文件指针;将此文件指针保存在一个文件指针变量中,以后所有对文件的操作都通过此文件指针变量完成;直到关闭文件,文件指针指向的文件类型变量释放。
例如:
定义一个指向文件的指针变量fp。
*/
fp=fopen(“mydata.txt”,...);/*打开文件时,系统开辟一个文件变量,并返回文件
指针,将此指针赋值(保存)给文件指针变量fp*/
......(文件操作函数,会引用文件指针fp)
fclose(fp);/*关闭文件,释放文件指针fp指向的文件变量*/
11.3文件的打开与关闭
对文件的操作的步骤:
先打开,后读写,最后关闭。
文件的打开(fopen函数)
1、文件的打开后才能进行操作,文件打开通过调用fopen函数实现。
调用fopen的格式是:
FFILE*fp;
fp=ffopen(文件名,打开方式或使用方式);
注意:
一定将函数返回的文件指针赋值给“文件指针变量”。
例如:
FILE*fp;
fp=fopen(“d:
\\a1.txt”,“r”);
说明:
(1)打开d:
盘根目录下文件名为a1.txt的文件,打开方式“r”表示只读。
(2)fopen函数返回指向d:
\a1.txt的文件指针,然后赋值给fp,fp->此文件,即fp与此文件关联。
(3)关于文件名要注意:
文件名包含文件名.扩展名;路径要用“\\”表示。
(4)关于打开方式,可以参看p204,表12-1,p205,表12-2。
简单地看,文件打开方式包含下面几类表示打开方式的关键词,不同类的可以组合。
●“r,w,a”:
读、写、添加
●“b,t”:
二进制,文本文件。
默认为文本方式,即没有b就是以文本方式打开文件。
●“+”:
可读写
2、文件打开方式(使用方式)的说明
(1)文件打开一定要检查fopen函数的返回值。
因为有可能文件不能正常打开。
不能正常打开时fopen函数返回NULL。
可以用下面的形式检查:
if((fp=fopen(...))==NULL){printf(“erroropenfile\n”);exit
(1);}
(2)“r”方式:
只能从文件读入数据而不能向文件写入数据。
该方式要求欲打开的文件已经存在。
(3)“w”方式:
只能向文件写入数据而不能从文件读入数据。
如果文件不存在,创建文件,如果文件存在,原来文件被删除,然后重新创建文件(相当覆盖原来文件)。
(4)“a”方式:
在文件末尾添加数据,而不删除原来文件。
该方式要求欲打开的文件已经存在。
(5)“+”(“r+,w+,a+”):
均为可读、可写。
但是“r+”,“a+”要求文件已经存在,“w+”无此要求;“r+”打开文件时文件指针指向文件开头,“a+”打开文件时文件指针指向文件末尾。
(6)“b、t”:
以二进制或文本方式打开文件。
默认是文本方式,t可以省略。
读文本文件时,将“回车”/“换行”转换为一个“换行”;写文本文件时,将“换行”转换为“回车/换行”。
(7)程序开始运行时,系统自动打开三个标准文件:
标准输入,标准输出,标准出错输出。
一般这三个文件对应于终端(键盘、显示器)。
这三个文件不需要手工打开,就可以使用。
标准文件:
标准输入,标准输出,标准出错输出对应的文件指针是stdin,stdout,stderr。
文件的关闭(fclose函数)
文件使用完毕后必须关闭,以避免数据丢失。
格式:
fclose(文件指针);
11.4文件的读写操作
一般用文本方式打开文件
常用的文件读写函数:
1、
字符读写函数:
fgetc,fputc
2、
一般用二进制方式打开文件
字符串读写函数:
fgets,fputs
3、格式化读写函数:
fscanf,fprintf
4、数据块读写函数:
fread,fwrite
说明:
1、这些函数都以f(file)开头。
2、这些函数都要使用打开文件时获得的文件指针。
3、前三类函数与标准I/O函数使用基本相同。
4、一般,对于文本文件使用“顺序访问”方式操作,对于二进制文件使用“随机访问”方式操作。
11.4.1字符读写函数
1、写一个字符到磁盘文件:
格式:
fputc(ch,fp)
功能:
将字符ch(可以是字符表达式,字符常量、变量等)写入fp所指向的文件。
返回:
输出成功返回值-输出的字符ch;输出失败-返回EOF(-1)。
其它说明:
每次写入一个字符,文件位置指针自动指向下一个字节。
例:
从键盘输入一行字符,写入到文本文件string.txt中。
(P.206)
#include
main()
{
FILE*fp;
charch;
if((fp=fopen("string.txt","w"))==NULL)/*打开文件string.txt(写)*/
{
printf("can'topenfile\n");exit
(1);
}
do/*不断从键盘读字符并写入文件,直到遇到换行符*/
{
ch=getchar();/*从键盘读取字符*/
fputc(ch,fp);/*将字符写入文件*/
}while(ch!
='\n');
fclose(fp);/*关闭文件*/
}_
2、从磁盘文件读一个字符
格式:
ch=fgetc(fp)
功能:
从fp所指向的文件读一个字符,字符由函数返回。
返回的字符可以赋值给ch,也可以直接参与表达式运算。
返回:
输入成功返回值-输入的字符;遇到文件结束-返回EOF(-1)。
其它说明:
(1)每次读入一个字符,文件位置指针自动指向下一个字节。
(2)文本文件的内部全部是ASCII字符,其值不可能是EOF(-1),所以可以使用EOF(-1)确定文件结束;但是对于二进制文件不能这样做,因为可能在文件中间某个字节的值恰好等于-1,如果此时使用-1判断文件结束是不恰当的。
为了解决这个问题,ANSIC提供了feof(fp)函数判断文件是否真正结束。
(3)feof函数既适合文本文件,也适合二进制文件文件结束的判断。
例:
将磁盘上一个文本文件的内容复制到另一个文件中。
(P.207)
#include
main()
{
FILE*fp_in,*fp_out;
charinfile[20],outfile[20];
printf("Entertheinfilename:
");
scanf("%s",infile);/*输入欲拷贝源文件的文件名*/
printf("Entertheoutfilename:
");
scanf("%s",outfile);/*输入拷贝目标文件的文件名*/
if((fp_in=fopen(infile,"r"))==NULL)/*打开源文件*/
{
printf("can'topenfile:
%s",infile);exit
(1);
}
if((fp_out=fopen(outfile,"w"))==NULL)/*打开目标文件*/
{
printf("can'topenfile:
%s",outfile);exit
(1);
}
while(!
feof(fp_in))/*若源文件未结束*/
{
fputc(fgetc(fp_in),fp_out);/*从源文件读一个字符,写入目标文件*/
}
fclose(fp_in);/*关闭源、目标文件*/
fclose(fp_out);
}_
将源文件、目标文件的文件名通过main函数的参数传递给程序,这样可以在命令行直接将文件名输入。
#include
voidmain(intargc,char*argv[])
{
FILE*fp_in,*fp_out;
if(argc<3)
{
printf("missingfilename\n");exit
(1);
}
if((fp_in=fopen(argv[1],"r"))==NULL)
{
printf("can'topenfile:
%s",argv[1]);exit
(2);
}
if((fp_out=fopen(argv[2],"w"))==NULL)
{
printf("can'topenfile:
%s",argv[2]);exit(3);
}
while(!
feof(fp_in))
{
fputc(fgetc(fp_in),fp_out);
}
fclose(fp_in);
fclose(fp_out);
}_
11.4.2字符串读写函数
1、从磁盘文件读一个字符串
●格式:
char*fgets(char*str,intn,FILE*fp)
●功能:
从fp所指向的文件读n-1个字符,并将这些字符放到以str为起始地址的单元中。
如果在读入n-1个字符结束前遇到换行符或EOF,读入结束。
字符串读入后最后加一个‘\0’字符。
●返回:
输入成功返回值-输入串的首地址;遇到文件结束或出错-返回NULL。
例:
编制一个将文本文件中全部信息显示到屏幕的程序。
使用fgets的例子。
#include
voidmain(intargc,char*argv[])
{
FILE*fp;
charstring[81];/*最多保存80个字符,外加一个字符串结束标志*/
if(argc!
=2||(fp=fopen(argv[1],"r"))==NULL)/*打开文件*/
{
printf("can'topenfile");exit
(1);
}
while(fgets(string,81,fp)!
=NULL)
/*如果未读到文件末尾(EOF),函数不会返回NULL,继续循环(执行循环体)*/
/*从文件一次读80个字符,遇换行或EOF,提前带回字符串*/
printf("%s",string);/*打印串*/
fclose(fp);/*关闭文件*/
}_
2、写一个字符串到磁盘文件
●格式:
fputs(char*str,FILE*fp)
●功能:
向fp所指向的文件写入以str为首地址的字符串。
●返回:
输入成功返回值-0;出错-返回非0值。
例:
在文本文件string.txt末尾添加若干行字符(P.210)。
使用fputs的例子。
#include
voidmain()
{
FILE*fp;
chars[81];
if((fp=fopen("string.txt","a"))==NULL)/*打开文件*/
{
printf("can'topenfile\n");exit
(1);
}
while(strlen(gets(s))>0)/*从键盘读入一个字符串,遇到空行(strlen=0)结束*/
{
fputs(s,fp);/*将字符串写进文件*/
fputs("\n",fp);/*补一个换行符*/
}
fclose(fp);/*关闭文件*/
}_
11.4.3格式化读写函数
格式化文件读写函数fprintf,fscanf与函数printf,scanf作用基本相同,区别在于fprintf,fscanf读写的对象是磁盘文件,printf,scanf读写的对象是终端。
格式:
fprintf(fp,格式字符串,输出表列);
fscanf(fp,格式字符串,输入表列);
其中:
fp是文件指针。
11.4.5数据块读写函数(一般用于二进制文件读写)。
(重点)
从文件(特别是二进制文件)读写一块数据(如一个数组元素,一个结构体变量的数据-记录)使用数据块读写函数非常方便。
数据块读写函数的调用形式为:
intfread(void*buffer,intsize,intcount,FILE*fp);
intfwrite(void*buffer,intsize,intcount,FILE*fp);
其中:
(1)buffer是指针,对fread用于存放读入数据的首地址;对fwrite是要输出数据的首地址。
(2)size是一个数据块的字节数(每块大小),count是要读写的数据块块数。
(3)fp文件指针
(4)fread、fwrite返回读取/写入的数据块块数。
(正常情况=count)
(5)以数据块方式读写,文件通常以二进制方式打开。
例如:
floatf[2];
FILE*fp=fopen(“...”,”r”);
fread(f,4,2,fp);/*或fread(f,sizeof(float),2,fp);*/
例:
从键盘输入一批学生的数据,然后把它们转存到磁盘文件stud.dat中。
P.211.
解:
#include
#include
#include
structstudent
{
intnum;
charname[20];
charsex;
intage;
floatscore;
};/*共5个成员,占用29bytes*/
main()
{
structstudentstud;
charnumstr[20],ch;
/*numstr-临时字符串,保存学号/年龄/成绩,然后转换为相应类型;ch-Y/N*/
FILE*fp;
if((fp=fopen("stud.dat","wb"))==NULL)/*以二进制、写方式打开文件*/
{
printf("can'topenfilestud.dat\n");
空读(有读取动作,但不保存读取的值-‘\n’)
键盘输入数据,保存在结构体变量中
exit
(1);
}
do
{
printf("enternumber:
");gets(numstr);stud.num=atoi(numstr);
printf("entername:
");gets(stud.name);
printf("entersex:
");stud.sex=getchar();getchar();
printf("enterage:
");gets(numstr);stud.age=atoi(numstr);
printf("enterscore:
");gets(numstr);stud.score=atof(numstr);
/*每次将一个准备好的结构体变量的所有内容写入文件(写一个记录)*/
fwrite(&stud,sizeof(structstudent),1,fp);
printf("haveanotherstudentrecord(y/n)?
");ch=getchar();getchar();
}while(toupper(ch)=='Y');/*循环读数据/写记录*/
fclose(fp);/*关闭文件*/
}_
说明:
(1)空读:
在输入字符,并按回车后,实际缓冲中有两个字符(如‘f/m’和‘\n’),只要前面有意义的字符(‘f/m’)。
可以用“空读”略过‘\n’。
(2)什么情况要空读?
如果后面的读取键盘是读取数字(整数/浮点数),不必空读;如果后面的读取键盘是读字符或字符串,应当“空读”。
(3)思考:
如果按照教材p212输入两个记录的数据,那么最后产生的文件长度是多少?
(58bytes)
备注:
C语言即使写文本文件,关闭时,也不自动加文件结束符。
11.5文件的定位
对文件的读写可以顺序读写,也可以随机读写。
1、文件顺序读写:
从文件的开头开始,依次读写数据。
(从文件开头读写直到文件尾部)
2、文件随机读写(文件定位读写):
从文件的指定位置读写数据。
3、文件位置指针:
在文件的读写过程中,文件位置指针指出了文件的当前读写位置(实际上是:
下一步读写位置),每次读写后,文件位置指针自动更新指向新的读写位置(实际上是:
下一步读写位置)。
注意区分:
文件位置指针,文件指针。
可以通过文件位置指针函数,实现文件的定位读写。
文件位置指针函数有:
●rewind重返文件头函数
●fseek位置指针移动函数
●ftell获取当前位置指针函数
11.5.1rewind-重返文件头函数
功能:
使文件位置指针重返文件的开头。
例:
有一个文本文件,第一次使它显示在屏幕上,第二次把它复制到另外一个文件中。
解:
#include
main()
{
FILE*fp1,*fp2;
fp1=fopen("string.txt","r");/*打开文件*/
fp2=fopen("string2.txt","w");
/*从文件string.txt读出,写向屏幕*/
while(!
feof(fp1))putchar(getc(fp1));
/*重返文件头*/
rewind(fp1);
/*从文件string.txt读出,写向文件string2.txt*/
while(!
feof(fp1))putc(getc(fp1),fp2);
fcloseall();/*关闭文件*/
}
11.5.2fseek-位置指针移动函数
功能:
移动文件读写位置指针,以便文件的随机读写。
格式:
fseek(FILE*fp,longoffset,intwhence);
参数:
(1)fp-文件指针.
(2)whence-计算起始点(计算基准).计算基准可以是下面符号常量:
符号常量
符号常量的值
含义
SEEK_SET
0
从文件开头计算
SEEK_CUR
1
从文件指针当前位置计算
SEEK_END
2
从文件末尾计算
(3)offset-偏移量(单位:
字节).从计算起始点开始再偏移offset,得到新的文件指针位置。
offset为正,向后偏移;offset为负,向前偏移。
例如:
fseek(fp,100,0);/*将位置指针移动到:
从文件开头计算,偏移量为100个字节的位置*/
fseek(fp,50,1);/*将位置指针移动到:
从当前位置计算,偏移量为50个字节的位置*/
/*向前移动*/
fseek(fp,-30,1);/*将位置指针移动到:
从当前位置计算,偏移量为-30个字节的位置*/
/*向后移动*/
fseek(fp,-10,2);/*将位置指针移动到:
从文件末尾计算,偏移量为-10个字节的位置*/
/*向后移动*/
例:
编程读出文件stu.dat中第三个学生的数据。
#include