版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
文件输入/输出
——C程序与文件交互数据的方式11.1文件的基本概念11.2文件的打开与关闭11.3顺序读/写数据文件11.4随机读/写数据文件11.5文件读/写的出错检测实操训练课外练习
11.1文件的基本概念
程序文件与数据文件有何异同?程序文件通过什么方式与数据文件交互?文件是指存储在外部介质上数据的集合。文件有不同的类型。在程序设计中,主要用到程序文件和数据文件。程序文件包括源程序文件(后缀为.c)、目标文件(后缀为.obj)、可执行文件(后缀为.exe)等。这种文件的内容都是程序代码。
数据文件的内容是供程序运行时读/写的数据。
每个文件都有一个唯一的文件标识符,便于识别和使用。文件标识包括3个部分,即文件路径、主文件名和扩展名(也称为后缀)。例如:D:\User\progame\file1.dat文件路径主文件名文件扩展名
文件路径表示文件的存储位置。文件名包括主文件名和文件扩展名。主文件名是文件的主要标识符号,由用户命名,扩展名(后缀)用来表示文件的性质,一般不超过3个字符,一般采用一些约定的符号。例如dat(数据文件)、txt(文本文件)、c(C语言源程序)、cpp(C++语言源程序)、exe(可执行文件)等。
11.1.1数据文件的概念
前面各章中,程序中数据的输入和输出都以终端为对象。键盘是常用的输入终端,显示器是常用的输出终端。程序中的数据不仅可以来自终端,也可以先建立数据文件,从文件中读取;处理的结果数据不仅可以在终端上显示,也可以文件形式存放到磁盘文件中,供以后使用。如果文件中存放的都是数据,就称为“数据文件”。
数据在文件中的存储形式是:字符以ASCII码形式存储,即一个字符占一个字节;数值型数据可以按数字的ASCII码形式存储,即一位数字的ASCII码占一个字节,也可以按二进制数存储。如果一个文件都以字符的ASCII码形式存储数据,则称之为文本文件;如果一个文件都以二进制形式存储数据,则称之为二进制文件。文本文件(ASCII码)中,一个数据的每一位数字的ASCII码占一个字节,调入内存时需转换成二进制数,存入文件时又要转换成ASCII码,占用内存空间大,而且有转换时间,但可直接向外设输出。二进制文件中数据的存储形式与内存存储数据的形式一致,占用空间小,从文件输入或向文件输出时不需要转换,但向外设输出时需要转换成ASCII码。
11.1.2文件缓冲区
所谓文件缓冲区,是系统与磁盘文件传输数据时在内存中开辟的一个暂存区域。向磁盘文件输出数据时,必须先送到缓冲区,装满缓冲区后,才送到磁盘文件中。从磁盘读入数据时,则从磁盘文件输入数据装满缓冲区,然后从缓冲区将数据逐个地送到程序数据区(给程序变量),如图11.1所示。缓冲区的大小由各个具体的C编译系统确定。图11.1系统与磁盘文件传输数据示意图
11.1.3文件类型指针
程序中对文件的各种操作是通过文件指针进行的。每个文件被使用时,系统在内存中开辟一个“文件信息描述区”,用来存放文件的有关信息(如文件名、文件状态、文件使用方式、文件当前位置、文件缓冲区所剩余的字节数等)。该信息描述区是用一个结构体变量来实现的,其类型由系统定义,取名为FILE。例如,一种C编译系统的stdio.h中有以下的文件类型结构体定义:
不同的C编译系统的FILE类型包含的内容不尽相同,但都大同小异。用户可以在程序中使用该类型来定义文件指针变量,通过指针变量来引用文件信息。例如:
FILE*fp1;
定义fp1为一个指向FILE类型的指针变量。fp1指向某一个文件的文件信息区(一个结构体变量),通过该文件信息区中的信息能够访问该文件。如果程序中使用n个文件,一般应定义n个指针变量。
11.2文件的打开与关闭
怎样为读/写打开或关闭一个数据文件?对文件操作之前必须打开文件,操作结束后又要关闭文件。打开文件的目的是建立磁盘文件与文件指针的关系,为磁盘文件在内存中建立缓冲区。关闭文件的目的是防止文件中数据丢失,释放文件缓冲区。C语言提供了文件的打开与关闭的标准函数。
11.2.1打开文件
使用标准函数fopen来打开一个文件,函数调用的一般形式为
fopen("文件名","文件使用方式");
函数的功能:把指定的文件按使用方式打开,系统自动给该文件建立信息描述区,把文件信息区的起始地址作为返回值。通常是将返回地址赋给一个指向文件的指针变量。如果打开文件不成功,则返回值为0。
使用文件的方式有多种,见表11.1。
例如:
FILE*fp;
fp=fopen("file.txt","r");
为读文件而打开文本文件file.txt,文件打开成功则将该文件的指针赋给fp。
11.2.2文件的关闭
使用标准函数fclose来关闭一个已经打开的文件,函数调用的一般形式为
fclose(文件指针);
函数的功能:解除文件指针与文件的联系,释放缓冲区。如果文件关闭成功则返回值为0,否则返回EOF(-1)。
11.3顺序读/写数据文件
怎样读/写一个数据文件?打开文件后,就可以进行读/写操作了。顺序读/写数据文件就是按文件中数据的顺序进行读/写。读数据时,从文件头开始一个接一个读取;写数据时,把数据追加在当前数据的后面。C语言提供了不同的读/写数据文件的方式。
11.3.1字符方式读/写文件
字符方式读/写文件是一次从文件中读/写一个字符。可通过文件的字符输入/输出函数调用来实现字符读/写操作。
1.读入字符函数fgetc
该函数调用的一般形式为
fgetc(fp);
fp是指向要输入数据文件的指针变量。其功能是从fp所指向的文件中读取一个字符,返回字符值;如果读取失败,则返回文件结束标志EOF(-1)。
2.写入字符函数fputc
该函数调用的一般形式为
fputc(ch,fp);
其中,ch是要写入文件的字符,可以是字符常量、字符变量或字符表达式等;fp是要写入文件的指针变量。其功能是将字符写入fp所指向的文件,写入成功则返回值是输出的字符,写入失败则返回EOF(-1)。
例11.1从键盘输入一行字符,写入file1.dat文件中。
编程思路:用fgetc函数从键盘逐个输入字符,再用fputc函数写到磁盘文件中。
分析:运行程序后,在当前目录下可查找到“file1.dat”,打开文件可看到内容是输入的字符串。if语句中使用“fopen("file1.dat","w")”函数调用,先建立一个新文件,将指针赋给文件指针变量fp。在循环中将输入的一个字符写入fp所指向的文件中。
例11.2将一个磁盘文件中的内容复制到另一个磁盘文件中。现将例11.1建立的文件file1.dat复制到文件file2.dat中。
编程思路:从file1.dat中逐个字符读出,再写入到file2.dat中。
分析:f_in=fopen("file1.dat","r")是打开已存在的文件,使f_in指向file1.dat。fopen("file2.dat","w")是打开不存在的文件,则建立新文件,将返回建立的文件地址赋给f_out。在while循环中的表达式测试文件是否到达结尾,未到结尾,从file1.dat读取f_in指向的字符,写入file2.dat中f_out指向的位置,同时输出当前读/写的字符。运行程序后,在当前目录下可查找到“file1.dat”和“file2.dat”,打开文件可看到两个文件内容相同。
11.3.2字符串方式读/写文件
字符串方式读/写文件是一次从文件中读/写一个字符序列。可通过文件的字符串输入/输出函数调用来实现字符串的读/写操作。
1.读取字符串函数fgets
该函数调用的一般形式为
fgets(str,n,fp);
其中,str是数组名(数组指针);n是字符串长度(包含结束符);fp是要读文件的指针变量。
函数功能:从fp所指向文件中读取n-1个字符串,并在最后加一个字符串结束符“\0”,存入字符数组str中。读取成功,返回数组指针str,失败则返回NULL。
2.写入字符串函数fputs
该函数调用的一般形式为
fputs(str,fp);
其中,str是数组名(数组指针);fp是要写入文件的指针变量。
函数功能:将str中的字符串写入fp所指向的文件中。写入成功返回0,写入失败则返回非0。
例11.3从键盘输入多个字符串,对它们按字母大小的顺序排序,然后把排好序的字符串存到文件中。
编程思路:
(1)从键盘输入多个字符串并存入一个二维数组中;
(2)对字符数组中的n个字符串按字母顺序排序,仍存在原字符数组中;
(3)将字符数组中的字符串输出到文件中。
分析:
(1)用单重循环实现3个字符串的输入,分别存入二维数组中的3个行一维数组中。
(2)利用双重循环实现3个字符串的比较和交换,将排好序的串,按序存入3个行一维数组中。
(3)用fputs函数调用,将排好序的3个行一维数组中的字符串写入文件。向文件中写入数据时,只写入字符串的有效字符,不包括字符串结束标志,所以“fputs(str[i],fp);”存串之后用“fputs("\n",fp);”将串结束标志加存在串后。
11.3.3用格式化方式读/写文件
C语言系统提供了与终端格式化输入/输出函数scanf、printf相仿的文件格式化输入/输出函数fscanf、fprintf。这两个函数调用的一般形式为
fprintf(文件指针,格式字符,输出列表);
fscanf(文件指针,格式字符,输入列表);
其中,“格式字符”“输入列表”“输出列表”都同scanf、printf函数。例如:
fprintf(fp,"%d,%6.2f",i,f);
语句把整型i和实型变量f的值按%d、%6.2f的格式输出到fp指向的文件中。
fscanf(fp,"%d,%f",&i,&f);
语句从fp所指文件中读取一个整型数据赋给变量i,读取一个实型数据赋给变量f。
用格式化读/写函数fscanf、fprintf对磁盘文件进行读/写,比较直观,容易理解,但因输入时要将ASCII码转换成二进制形式,再保存在变量中,输出时又要将二进制形式转换成ASCII码,所以花费的时间较多。因此,在内存与磁盘交换数据频繁的情况下,最好不用fscanf、fprintf函数,而用下面介绍的fread、fwrite函数进行二进制的读/写。
11.3.4用二进制方式向文件读/写一组数据
C语言提供的fread和fwrite函数能实现一次输入/输出一个数据块,以二进制形式进行读/写。
这两个函数调用的一般形式为
fread(buffer,size,n,fp);
fwrite(buffer,size,n,fp);
参数说明:
“buffer”表示缓冲区地址。对fread,表示从文件中读出数据存放在内存缓冲区首地址;对fwrite,表示向文件输出数据的内存缓冲区首地址。
“size”表示要读/写的数据的存储长度(字节数)。
“n”表示要读/写数据的个数。
“fp”表示要读/写的文件指针。
fread的功能是从fp所指向的文件中读取n个数据(每个数据有size个字节),存到buffer为首地址的存储区中。如果读数据成功,返回输入数据的个数n。如遇文件结束或出错则返回值0。
fwrite的功能是把buffer为首地址的n个数据(每个数据有size个字节)写到fp所指向的文件中。如果写数据成功,返回输出数据的个数n。
例11.4从键盘输入3个学生的相关数据,存到一个文件中,再从文件中读取数据,输出到屏幕上。
编程思路:定义有3个元素的结构体数组,用来存放3个学生的数据。在主函数中输入3个学生的数据。用save函数实现向磁盘文件输出学生数据。在主函数中从磁盘文件读取学生数据输出到屏幕上。
分析:
(1)在main()函数中输入3个学生数据,存入结构体数组stud1中。调用save函数将stud1中的数据输出到“stu.dat”文件中。fwrite将一个结构体数据送到“stu.dat”文件中。结构体长度用“sizeof(structstudent)”测试得到,得到的测试值为36(数据长度=4+10+4+15=33,但按字节的整数倍存储)。
(2)在save函数中的“fopen("stu.dat","wb")”是按二进制写方式打开文件,此时文件不存在,建立一个新文件。在main函数中的“fopen("stu.dat","rb")”是为了读而打开二进制文件。fread函数从“stu.dat”文件中读取一个结构体数据(一个学生数据)存入结构体数组stud2中,采用数组元素和结构体成员的输出方式输出到屏幕上。
(3)只要修改宏定义中常数3,可实现任意多学生数据的输入、存储和输出。
11.4随机读/写数据文件
怎样按用户需要读/写数据文件中任一数据?顺序读/写数据文件是按数据在文件中的存储顺序进行读/写。随机读/写数据文件,是读/写文件中任一位置的数据。
为了实现数据文件的读/写控制,系统给文件内部设置了一个位置指针。位置指针总是指向当前要读/写数据的位置。顺序读/写方式中,位置指针是由系统控制的。读/写总是从文件开头进行,每读/写一个数据,指针就后移一个数据位置,指向下一个数据位置,一直到文件尾部,指针又重返文件开头。随机读/写就是把位置指针控制在需要的位置上进行读/写的方式。随机读/写文件的关键是控制文件位置指针,也就是指针定位。C语言提供了文件位置指针定位的函数,利用这些函数,就可方便地实现随机读/写文件。
11.4.1位置指针定位函数
C语言提供了文件定位、测试和移动函数。
1.设置文件位置指针到文件开头函数rewind
该函数调用的一般形式为
rewind(fp);
其中,参数fp是文件类型指针。
函数功能:将fp所指向文件中的位置指针置于文件的开头位置,并清除文件结束标志和出错标志。函数无返回值。
2.检测文件是否结束函数feof
该函数调用的一般形式为
feof(fp);
函数功能:检测是否到fp所指向文件的结束位置,检测到文件结束,返回非0值,否则返回0。
3.检测文件位置指针的当前位置ftell
该函数调用的一般形式为
ftell(fp);
函数功能:检测fp所指向文件的当前读/写位置,返回一长整型数,表示当前读/写位置距文件开头的偏移量。偏移量是指当前读/写位置距离文件开头的字节数。
4.设置文件位置指针函数fseek
该函数调用的一般形式为
fseek(fp,offset,base);
其中,offset表示偏移量;base表示改变位置指针的相对基准位置,参数的取值见表11.2。
函数功能:fp所指向文件的位置指针移到以“base”所给出的位置为基准、以“offset”为偏移量的位置。移动成功返回当前位置,否则返回-1。
注意:文件位置指针的取值是距离文件开头的字节数,即偏移量。知道了偏移量也就确定了读/写位置。这与文件指针是截然不同的两个概念,不能混淆。
11.4.2随机读/写文件
利用文件位置指针定位函数就可以根据需要定位读/写位置,实现随机读/写文件中的数据。下面通过例子来说明文件位置指针定位函数的使用及程序设计。
例11.5在磁盘文件中存有10个学生的数据,要求读取第1、3、5、7、9个学生数据并在屏幕上显示。
编程思路:
(1)利用例11.4程序建立10个学生数据文件;
(2)按“二进制只读”方式打开学生数据文件,准备从文件中读取数据;
(3)将文件位置指针指向文件开头,读取第1个学生数据,输出到屏幕上;
(4)分别使文件位置指针指向第3、5、7、9个学生数据区的开始位置,读取一个学生数据,并输出到屏幕上。
分析:本例是在例11.4程序建立的学生数据文件的基础上,用“fopen("stu.dat","rb")”打开文件,即以“二进制只读”方式打开文件;采用步长为2的循环和“i*sizeof(structstudent)”确定读/写位置偏移量,使位置指针指向第1、3、5、7、9个学生数据;把学生数据读入到结构体数组中,然后再按结构体数组元素输出。
11.5文件读/写的出错检测
怎样检测和处理数据文件读/写中的出错信息?文件读/写是和磁盘打交道,常会出现因磁盘的问题导致操作失败。因此需要检测功能,及时识别问题所在,以便调整操作或排除故障。文件读/写函数具有出错检测功能,通过返回值来识别。C语言系统还提供了专门的错误检测函数和出错标志清除函数。
1.错误检测函数ferror
该函数调用的一般形式为
ferror(fp);
函数功能:检测fp所指向的磁盘文件是否可正常读/写,如果不能正常读/写,返回一个非0值;能正常读写,返回值0。
执行fopen函数调用时,ferror函数的初始值自动为0。应注意,对同一文件每次调用输入/输出函数,都会产生一个新的ferror函数值,因此,在调用一个输入/输出函数后,应立即检查ferror函数值,否则信息会丢失。
2.出错标志清除函数clearerr
一旦文件出错一次,出错标志就一直保留,使后续的输入/输出无法正常进行。因此,应及时清除出错标志。该函数调用的一般形式为
clearerr(fp);
函数功能:将fp所指向的文件出错标志置为0。
实操训练
实训任务十一学习程序与数据文件交互的程序设计方法实训项目1建立学生班级成绩表数据文件,成绩表式样如表11.3所示,实现以下功能:
(1)从键盘输入学生数据,存入学生结构体数组中。
(2)求每个学生的平均成绩,存到学生结构体平均分字段中。
(3)建立数据文件,存储学生成绩表数据。
(4)输出数据文件的学生成绩表。
输入/输出界面可参照图11.2所示。图11.2实训项目1界面式样
实训指导
1.设计程序
(1)根据学生成绩表式样定义结构体类型及数组,同时定义文件指针,将其定义为全局数据对象。
(2)从键盘输入学生成绩表数据,存入结构体数组中,可用一个函数来实现。函数形参设置为结构体数组,循环一次输入一条学生记录,在循环中嵌套内循环,依次实现每门课成绩输入。输入的数据存入学生结构体对应字段中。
(3)计算每个学生的平均成绩,可用一个函数来实现。函数形参设置为结构体数组,用双重循环依次实现每个学生各科目成绩求和,并计算出平均成绩,存入学生结构体的平均分字段中。
(4)创建学生成绩表数据文件,可用一个函数来实现。函数形参设置为文件指针(file*x),循环一次把一条学生记录(结构体数组的一个元素)写入指针所指向的文件中。
(5)输出数据文件中学生成绩表数据,可用两个函数来实现。先设计结构体数组的一个元素(一条学生记录)输出的函数,形参设置为结构体变量,依次按成员顺序输出,其中学生成绩是数组,需用内循环依次按科目输出。再设计数据文件中的结构体数组(全部学生记录)输出的函数,函数形参设置为文件指针(file*x),用循环实现从文件读取一条学生记录,赋给结构体数组元素,调用结构体的一个元素的输出函数。
(6)在主函数中定义指针,并指向学生结构体数组,依次调用输入、求平均值、建立数据文件、输出数据文件数据的函数。调用函数前应正确设置实参。调用输入和求平均值函数时,实参是学生结构体指针;调用建立数据文件函数和输出数据文件数据函数前,应打开文件,文件指针作实参。
2.调试运行程序
(1)从键盘输入班级学生成绩表数据,检测结果是否正确。
(2)在源文件目录下,查看所建立的学生数据文件。
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
评论
0/150
提交评论