第十六章 文 件.docx
- 文档编号:27439518
- 上传时间:2023-07-01
- 格式:DOCX
- 页数:18
- 大小:27.41KB
第十六章 文 件.docx
《第十六章 文 件.docx》由会员分享,可在线阅读,更多相关《第十六章 文 件.docx(18页珍藏版)》请在冰豆网上搜索。
第十六章文件
第十六章文件
16.1C语言文件的概念
在此之前,所有输入输出只涉及到键盘和显示器。
在运行C程序时我们通过键盘输入数据,并借助显示器把程序的运算结果显示出来。
但是,计算机作为一种先进的数据处理工具,它所面对的数据信息量十分庞大。
仅依赖于键盘输入和显示输出等方式是完全不够的,通常,解决的办法是将这些数据记录在某些介质上,利用这些介质的存储特性,携带数据或长久地保存数据,这种记录在外部介质上的数据的集合称为“文件”。
其实,我们对文件并不陌生,在本书的开头,读者在编写C语言的简单程序时,就知道在TURBOC的集成环境下或在某些编辑系统中将源程序输入到计算机里去,然后把它们以文件的形式存储到磁盘上,这些文件我们称之为源程序文件,或叫文本文件,磁盘文件等。
计算机的文件分类方法有很多,本章我们仅讨论通过C程序的输入、输出操作所涉及的、存储在外部介质上的文件,这类文件通常称为“数据文件”,并以磁盘作为文件的存储介质。
在程序中,当调用输入函数从外部文件中输入数据赋给程序中的变量时,这种操作称为“输入”或“读”;当调用输出函数把程序中变量的值输出到外部文件中时,这种操作称为“输出”或“写”。
C语言中,对于输入、输出的数据都按“数据流”的形式进行处理,也就是说,输出时,系统不添加任何信息,输入时,逐一读入数据,直到遇到EOF或文件结束标志。
C程序中的输入、输出文件,都以数据流的形式存储在介质上。
对文件输入输出方式也称“存取方式”。
C语言中,有两种对文件的存取方式:
顺序存取和直接存取。
顺序存取文件的特点是:
每当“打开”这类文件,进行读或写操作时,总是从文件的开头开始,从头到尾顺序地读或写;也就是说当顺序存取文件时,要读第n个字节时,先要读取前n-1个字节,而不能一开始就读到第n个字节:
要写第n个字节时,先要写前n-1个字节。
直接存取文件又称随机存取文件,其特点是:
可以通过调用C语言的库函数去指定开始读(写)的字节号,然后直接对此位置上的数据进行读(写)操作。
数据可以按文本形式或二进制形式存放在介质上,因此可以按数据的存放形式分为文本文件和二进制文件。
这两种文件都可以用顺序方式或直接(随机)方式进行存取。
本章只讨论按这两种方式存储在磁盘介质上的文本文件和二进制文件。
所谓文本文件指的是,当输出时,数据按面值转换成一串字符,每个字符,以字符的ASCII代码值存储到文件中,一个字符占一个字节。
例如:
int类型的整数1234在内存中占两个字节,当把它以字符代码的形式存储到文件中时,系统将把它转换成1、2、3、4四个字符的ASCII码并把这些代码依次存入文件,在文件中占四个字节。
又如:
float类型数3.141592,在内存中占四个字节,系统将把它转换成3、.(点号)、1、4、1、5、9、2八个字符的ASCII码值存入文件,在文件中占八个字节。
在前面,当用printf函数进行输出时就进行了这样的转换,只是在内部处理过程中,指定了输出文件为终端屏幕。
反之,当输入时,又把指定的一串字符按类型转换成数据,并存入内存。
如当调用scanf函数进行输入时就进行了这种转换,只是在内部处理过程中,指定了键盘为输入文件。
当数据按二进制形式输出到文件中时,数据不经过任何转换、按计算机内的存储形式直接存放到磁盘上;也就是说对于字符型数据,每个字符占一个字节,对于int类型数据,每个数据占两个字节,float类型的每个数据占四个字节,其它依次类推;当从二进制文件中读入数据时,不必经过任何转换,而直接将读入的数据存入变量所占内存空间。
由此可见,因为不存在转换的操作,从而提高了对文件输入输出的速度。
注意:
不能将二进制数据直接输出到终端屏幕,也不能从键盘输入二进制数据。
ANSI标准规定,在对文件进行输入或输出的时候,系统将为输入或输出文件开辟缓冲区,所谓“缓冲区”,是系统在内存中为各文件开辟的一片存储区;当对某文件进行输出时,系统首先把输出的数据填入为该文件开辟的缓冲区内,每当缓冲区被填满时,就把缓冲区中的内容一次性地输出到对应文件中。
当从某文件输入数据时,首先将输入文件中输入一批数据放入到该文件的内存缓冲区中,输入语句将从该缓冲区中依次读取数据;当该缓冲区中的数据被读完时,将再从输入文件中输入一批数据放入缓冲区。
16.2 文件指针
文件指针,实际上是指向一个结构体类型的指针变量,这个结构体中包含有诸如:
缓冲区的地址、在缓冲区中当前存取的字符的位置、对文件是“读”还是“写”、是否出错、是否已经遇到了文件结束标志等信息。
用户不必去了解其中的细节,所有一切都在stdio.h头文件中进行了定义;并称此结构体类型名为FILE,可以用此类型名来定义文件指针。
定义文件类型指针变量的一般形式为:
FILE*指针变量名
例如:
FILE*fp1,*fp2;
fp1和fp2均被定义为指向文件类型的指针变量,称为文件指针。
16.3打开文件
在对文件进行读、写操作之前,首先要解决的问题是如何把程序中要读、写的文件与磁盘上实际的数据文件联系起来。
在C语言中,这并不困难,只需调用C语言提供的库函数fopen“打开”文件就可实现这些联系。
fopen函数的一般调用形式为:
fopen(文件名,文件使用方式);
函数返回一个指向FILE类型的指针。
例如:
FILE*fp
fp=fopen(“file_a”,”r”);
fopen函数调用中要求两个字符串作为参数。
第一个字符串中包含了进行读、写操作的文件名,用来指定所要打开的文件;在本例中,指定的文件名为:
file_a。
第二个字符串中指定了文件的使用方式,用户可通过这个参数来指定对文件的使用意图。
若以上函数调用成功,函数返回一个FILE类型的指针,赋给文件指针变量fp,从而把指针fp与文件file_a联系了起来,也就是说,在此调用之后,指针fp就指向了文件file_a。
无论哪种使用方式,当打开文件时出现了错误,fopen函数将返回NULL。
为了保证在程序中使用正确打开的文件,建议用以下程序段,当在打开文件,发生错误时,使程序逻辑停止运行:
if((fp=fopen(“file_a”,”r”))==NULL)
{printf(“Cannotopenthisfile!
\n”);
exit(0);
}
最常用的文件使用方式及其含义如下:
1.”r”。
为读而打开文本文件。
当指定这种方式时,对打开的文件只能进行“读”操作。
若指定的文件不存在,则会出错;另外一些情况如企图去读一个不允许读的文件时,也会出错。
2.”rb”。
为读而打开一个二进制文件。
其余功能与”r”相同。
3.”w”。
为写而打开文本文件。
这时,如果指定的文件不存在,系统将用在fopen调用中指定的文件名建立一个新文件;如果指定的文件已存在,则将从文件的起始位置开始写,文件中原有内容将全部消失。
4.”wb”。
为写而打开一个二进制文件。
其余功能与”w”相似,但从指定位置开始写。
5.”a”。
为在文件后面添加数据而打开文本文件。
这时,如果指定的文件不存在,系统将在fopen调用中指定的文件名建立一个新文件;如果指定的文件已存在,则文件中原有内容将保存,新的数据写在原有内容之后。
6.”ab”。
为在文件后面添加数据而打开一个二进制文件。
其余功能与”a”相同。
7.”r+”。
为读和写而打开文本文件。
用这种方式时,指定的文件应当已经存在。
既可以对文件进行读,也可对文件进行写,在读和写操作之间不必关闭文件。
只是对于文本文件来说,读和写总是从文件起始位置开始。
在写新的数据时,只复盖新数据所占的空间,其后的老数据并不丢失。
8.”rb+”。
为读和写而打开一个二进制文件。
功能与”r+”相同。
只是在读和写时,可以由位置函数设置读和写的起始位置,也就是说不一定从文件的起始位置开始读和写。
9.”w+”。
首先建立一个新文本文件,进行写操作,随后可以从头开始读。
如果指定的文件已存在,则原有的内容将全部消失。
10.”wb+”。
功能与”w+”相同二进制文件;只是在随后的读和写时,可以由位置函数设置读和写的起始位置。
11.”a+”。
功能与”a”相同;只是在文件尾部添加新的数据之后,可以从头开始读。
12.”ab+”。
功能与”a+”相同;只是在文件尾部添加新的数据之后,可以由位置函数设置开始读的起始位置。
当开始运行一个C程序时,系统将负责自动打开三个文件,这三个文件分别是标准输入文件、标准输出文件和标准出错文件,并规定相应的文件指针为stdin、stdout、stderr,它们已在stdio.h头文件中进行了说明。
通常,stdin和键盘联接、stdout和stderr和终端屏幕联接。
注意:
这些指针是常量、不是变量,因此不能重新赋值。
16.4关闭文件
当对文件的读(写)操作完成之后,必须将它关闭。
关闭文件可调用库函数fclose来实现,fclose函数的调用形式如下:
fclose(文件指针)
若fp是指向文件file_a的文件指针,当执行了fclose(fp);之后,若对文件file_a的操作方式为“读”方式,以上函数调用之后,使文件指针fp与文件file_a脱离关系。
可以重新分配文件指针fp去指向其它文件。
若对文件file_a的操作方式为“写”方式,则系统首先把该文件缓冲区中的剩余数据全部输出到文件中,然后使文件指针fp与文件file_a脱离联系。
由此可见,在完成了对文件的操作之后,应当关闭文件,否则文件缓冲区中的剩余数据就会丢失。
当成功地执行了关闭操作函数返回0,否则返回非0。
16.5调用getc(fgetc)和putc(fputc)函数进行输入和输出
当成功地打开文件之后,接下来的事情就是去对文件进行输入或输出操作。
最简单的是调用getc(或fgetc)和putc(或fputc)函数进行字符的输入和输出。
1.调用putc(或fputc)函数输出一个字符
putc函数的调用形式如下:
putc(ch,fp);
这里ch是待输出的某个字符,它可以是一个字符常量,也允许是一个字符变量。
fp是文件指针。
putc(ch,fp)的功能是将字符ch写到文件指针fp所指的文件中去。
当输出成功,putc函数返回所输出的字符;如果输出失败则返回一个EOF值。
EOF是在stdio.h库函数文件中定义的符号常量,其值等于-1。
fputc函数的调用形式和函数的功能与putc函数完全相同。
例16.1把从键盘输入的文本按原样输出到名为file_a.dat文件中,用字符@作为键盘输入结束标志。
以上操作的算法步骤十分简单:
(1)打开文件。
(2)从键盘输入一个字符。
(3)判输入的字符是否是字符@;若是,结束循环,执行步骤(7)。
(4)把刚输入的字符输出到文件中。
(5)从键盘输入一个字符。
(6)重复步骤(3)至(5)。
(7)关闭文件。
(8)程序结束。
程序如下:
main()
{FILE*fpout;
charch;
if((fpout=fopen("file_a.dat","w"))==NULL)
{printf("Cannotopenthisfile!
\n");exit(0);}
ch=getchar();
while(ch!
='@')
{fputc(ch,fpout);ch=getchar();}
fclose(fpout);
}
2.调用getc(或fgetc)函数输入一个字符
getc函数的调用形式如下:
ch=getc(pf);
此处pf是文件指针;函数的功能是从pf指定的文件中读入一个字符,并把它作为函数值返回。
以上表达式中getc函数把从文件中读入的一个字符赋给变量ch。
fgetc函数的调用形式和函数的功能与getc函数完全相同。
例16.2把一个已存在磁盘上的file_a.dat文本文件中的内容,原样输出到终端屏幕上。
算法步骤如下:
(1)打开文件。
(2)从指定文件中读入一个字符。
(3)读入的是否是文件结束标志;若是,结束循环,执行步骤(7)。
(4)把刚读入的字符输出到终端屏幕。
(5)从文件中再读入一个字符。
(6)重复步骤(3)至(5)。
(7)关闭文件。
(8)程序结束。
注意:
无论调用哪种函数读文件时,最好要先执行一次读操作,然后再去判文件是否结束。
程序如下:
#include"stdio.h"
main()
{FILE*fpin;
charch;
if((fpin=fopen("file_a.dat","r"))==NULL)
{printf("Cannotopenthisfile!
\n");exit(0);}
ch=fgetc(fpin);
while(ch!
=EOF)
{putchar(ch);ch=fgetc(fpin);}
fclose(fpin);
}
16.6判文件结束函数feof
例16.2中,程序从一个磁盘文件中逐个读取字符并输出到屏幕上显示,在while循环中以EOF作为文件结束标志,这种以EOF作为文件结束标志的文件,必须是文本文件。
在文本文件中,数据都是以字符的ASCII代码值的形式存放,我们知道,ASCII代码值的范围是0到255,不可能出现-1,因此可以用EOF作为文件结束标志。
当把数据以二进制形式存放到文件中时,就会有-1值的出现,因此不能采用EOF作为二进制文件的结束标志。
为解决这一问题,ANSIC提供一个feof函数,用来判断文件是否结束。
如果遇到文件结束,函数feof(fp)的值为1,否则为0。
feof函数既可用以判断二进制文件又可用以判断文本文件。
例16.3编写程序,用于把一个文本文件(源)复制到另一个文件(目的)中;源文件名和目的文件名由命令行输入。
命令行的形式如下:
可执行程序名源文件名存实亡目的文件名
例如:
我们编写的这个C程序的名为MYCOPY.C,经过编译、连接后,产生了一个名为MYCOPY.EXE的可执行文件;如果希望通过执行此程序把文件FILE_A.DAT中的内容复制到DILE_B.DAT中,命令行的形式应当如下:
MYCOPYFILE_A.DATFILE_B.DAT
程序中可用main函数中的参数把命令行中的字符串读入到程序中。
程序如下:
#include"stdio.h"
voidfilecopy(FILE*,FILE*);
main(intargc,char*argv[])
{FILE*fpin,*fpout;
if(argc==3)/*有三个命令行参数*/
{fpin=fopen(argv[1],"r");/*条开输入文件*/
fpout=fopen(argv[2],"w");/*打开输出文件*/
filecopy(fpin,fpout);/*把fpin所指文件中内容输出*/
/*到fpout所指定文件中*/
fclose(fpin);fclose(fpout);/*关闭输入、输出文件*/
}
else
if(argc>3)/*命令行参数太多*/
printf("Thefilenamestoomany!
!
\n");
else/*命令行参数不足*/
printf("TherearenofilenamesforInputoroutput!
!
\n");
}
voidfilecopy(FILE*fpin,FILE*fpout)
{charch;
ch=getc(fpin);/*先读一次文件再去判文件是否结束*/
while(!
feof(fpin))/*当未到文件末尾时进行循环*/
{putc(ch,fpout);ch=getc(fpin);}/*逐个字符进行复制*/
}
16.7fscanf函数和fprintf函数
1.fscanf函数
fscanf函数只能从文本文件中按格式输入。
fscanf函数和scanf函数相似,只是输入的对象是磁盘上文本文件中的数据。
函数的调用形式如下:
fscanf(文件指针,格式控制字符串,输入项表)
例如,若文件指针fp已指向一个已打开的文本文件,a、b分别为整型变量,则以下语句从fp所指的文件中读入两个整数放入变量a和b中:
fscanf(fp,”%d%d”,&a,&b);
注意:
文件中的两个整数之间用空格(或跳格符、回车符)隔开。
语句:
fscanf(std,”%d%d”,&a,&b);
等价于:
scanf(“%d%d”,&a,&b);
因为文件名stdin就是代表终端键盘。
2.fprintf函数
fprintf函数按格式将内存中的数据转换成对应的字符,并以ASCII代码形式输出到文本文件中。
fprintf函数和printf函数相似,只是输出的内容将按格式存放在磁盘的文本文件中。
函数的调用形式如下:
fprintf(文件指针,格式控制字符串,输出项表)
例如,若文件指针fp已指向一个已打开的文本文件,x、y分别为整型变量,则以下语句将把x和y两个整型变量中的整数按%d格式输出到fp所指的文件中:
fprintf(fp,”%d%d”,x,y);
注意:
为了以后便于读入,两个数之间应当用空格隔开。
同时也是为了以后便于读入,最好不要输出附加的其它字符串。
以下语句:
fprintf(stdout,”%d%d”,x,y);
等价于:
printf(“%d%d”,x,y);
因为文件名stdout就是代表终端屏幕。
16.8fgets函数和fputs函数
1.fgets函数
fgets函数用来从文件中读入字符串。
fgets函数的调用形式如下:
fgets(str,n,fp);
此处,fp是文件指针;str是存放字符串的起始地址;n是一个int类型变量。
函数的功能是从fp所指文件中读入n-1个字符放入str为起始地址的空间内;如果在未读满n-1个字符之时,已读到一个换行符或一个EOF(文件结束标志),则结束本次读操作,读入的字符串中最后包含读到的换行符。
因此,确切地说,调用fgets函数时,最多只能读n-1个字符。
读入结束后,系统将自动在最后加’\0’,并以str作为函数值返回。
2.fputs函数
fputs函数用来把字符串输出到文件中。
fputs函数的调用形式如下:
fputs(str,fp);
此处,fp是文件指针;str是待输出的字符串,可以是字符串常量、指向字符串的指针或存放字符串的字符数组名等。
用此函数进行输出时,字符串中最后的’\0’并不输出,也不自动加’\n’。
输出成功函数值为正整数,否则为-1(EOF)。
根据fputs函数操作特点,需要注意的是,调用函数输出字符串时,文件中各字符串将首尾相接,它们之间将不存在任何间隔符。
为了便于读入,在输出字符串时,应当注意人为地加入诸如”\n”这样的字符串。
16.9fread函数和fwrite函数
fread和fwrite函数用来读、写二进制文件。
它们的调用形式如下:
fread(buffer,size,count,fp);
fwrite(buffer,size,count,fp);
其中,buffer:
是数据块的指针,对fread来说,它是一个内存块的首地址,输入的数据存入这个内存块中;对于fwrite来说,它是准备输出的数据块的起始地址。
size:
表示每个数据块的字节数。
count:
用来指定每读、写一次,输入或输出数据块的个数(每个数据块具有size字节)。
fp:
文件指针。
例如有如下结构体:
structst
{charnum[8];
floatmk[5];
}pers[30];
假设pers数组的每个元素包含有学生的学号和五门课的成绩,并假设pers数组中30个元素中都已有值,文件指针fp所指文件已正确打开;执行以下循环把这30个元素中的数据输出到fp所指文件中。
for(i=0;i<30;i++)
fwrite(&pers[i],sizeof(structst),1,fp);
以上for循环中,每执行一次fwrite函数调用,就从&pers[i]地址开始输出由第三个参数指定的“1”个数据块,每个数据块含sizeof(structst)个字节二进制数;也就是一次输出一个结构体变量中的值。
也可以用以下步骤从以上建立的文件中再次将每个学生数据逐个读入到pers数组中。
这时,文件必需为“读“而打开。
i=0;
fread(&pers[i],sizeof(structst),1,fp);
while(!
feof(fp))
{i++;
fread(&pers[i],sizeof(structst),1,fp);
}
16.10文件定位函数
在介绍文件定位函数之前,我们先引入一个“文件位置指针”的概念。
“文件位置指针”和前面的“文件指针”完全是两个不同的概念。
文件指针是指在程序中定义的FILE类型的变量,通过fopen函数调用给文件指针赋值,使文件指针和某个文件建立联系,C程序中通过文件指针实现对文件的各种操作。
文件位置指针只是一个形象化的概念,我们将用文件位置指针来表示当前读或写的数据在文件中的位置。
当通过fopen函数打开文件时,可以认为文件位置指针总是指向文件的开头、第一个数据之前。
当文件位置指针指向文件末尾时,表示文件结束。
当进行读操作时,总是从文件位置指针所指位置开始,去读其后的数据,然后位置指针移到尚未读的数据之前,以务指示下一次的读(或写)操作。
当进行写操作时,总是从文件位置指针所指位置开始去写,然后移到刚写入的数据之后,以务指示下一次输出的起始位置。
16.10.1fseek函数
fseek函数用来移动文件位置指针到指定的位置上,接着的读或写操作将从此位置开始。
函数的调用形式如下:
fseek(pf,offset,origin)
此处,pf是文件指针;offset是以字节为单位的位移量,为长整型数;origin是起始点,用以指定位移量是以哪个位置为基准的,起始点既可用标识符来表示,也可用数字来代表。
表16.1给出了代表起始点的标识符和对应的数字。
表16.1位移量的表示方法及含义
标识符数字代表的起始点
SEEK_SET0文件开始
SEEK_END2文件末尾
SEEK_CUR1文件当前位置
对于二进制文件,当位移量为正整数时,表示位置指针从指定的起始点向文件尾部方向移动;当位移量为负整数时,表示位置指针从指定的起始点向文件首部方向移动。
假设pf已指向一个二进制文件;以下函数调用,使文件位置指针从文件的开头后移30个字节:
fseek(pf,30L,SEEK_SET)
若pf已指向一个二进制文件;以下函数调用,使文件位置指针从文件尾部前移10个sizeof(int)、即20个字节:
fseek(pf,-10L*sizeof(int),SEEK_END)
对于文本文件,位移量必须是0。
假设pf已指向一个文本文件;以下函数调用,使文件位置指针移到文件的开始:
fseek(pf,0L,SEEK_SET)
假设pf已指向一个文本文件;以下函数调用,使文件位置指针移到文件的末尾:
fseek(pf,0L,SEEK_END)
16.10.2ftell函数
ftell函数用以获得的文件当前位置指针的位置,函数组出当前位置指针相对于文件开头的字节数。
若fp已指向一正确打开的文件,函数调用形式如下:
longt;
t=ftell(fp);
当函数调用出错时,函数返回-1L。
当打开一个文件时,通常并不知道该文件的长度,通过以下函
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 第十六章 第十六