1、第第10章章 项目中文件的应用项目中文件的应用 在前面的章节中,我们学习了结构体这种构造数据类型,它主要是为了存储复杂的数据,在第9章中,我们通过学生信息管理系统来实现对批量数据的处理,但这些批量数据只能在程序执行时占据内存,程序结束后即从内存消失。那么添加学生信息等操作每次都要重新执行。如何将数据永久保存起来,即将输入输出的数据以磁盘文件的形式存储起来,这些内容是本章需要解决的问题。本章将结合学生信息管理系统项目中学生信息的存储和重载,学习文件的概念、分类、文件指针和文件操作等相关知识。学习目标:理解和掌握文件的概念、文件的打开、文件的关闭;理解和掌握文件的读写操作。10.1 任务二 项目中
2、数据的存储一、任务描述 在主函数中,通过InputStu()函数添加学生信息,添加完后,应将学生信息保存在磁盘文件中。在浏览、删除等操作中,首先要将数据读入到结构体数组中,对结构体数组进行操作,操作完成后,再将结构体数组中的数据保存到文件中。该任务将用SaveStu()函数将结构体数组存入到“list.dat”文件中,用LoadStu()函数实现将“list.dat”文件中的数据导入到结构体数组中。二、任务涉及知识要点 本任务涉及到的新知识点主要有文件的打开、读写、关闭等操作。三、任务分析 实现学生信息的处理和保存,可以用LoadStu()函数和SaveStu()函数实现读取和存储。该项目中虽
3、然定义了能够处理的最大学生数N,但是由于从文件中读取或者通过函数InputStu()输入的学生数量是不定的,所以在LoadStu()、InputStu()函数中均要统计读取或输入的学生数量。在调用SaveStu()函数进行数据保存时,也要将数组元素的个数作为实参传入,以确定要保存的数组元素个数。考虑到数据分多次录入的情况,SaveStu()可采用追加和覆盖两种方式写入文件。由于要对学生信息数组stu和学生实际人数同时进行传递,因此在函数的参数定义中采用结构体数组和指针,如void LoadStu(struct student stu,int*stu_number);,这样可以使学生信息结构体数
4、组stu和学生人数stu_number在各个函数之间进行传递。四、任务实现各函数的定义分别为:1.学生信息的读取函数void LoadStu(struct student stu,int*stu_number)FILE*fp;int i=0;if(fp=fopen(list.dat,rb)=NULL)printf(不能打开文件n);return;while(fread(&stui,sizeof(struct student),1,fp)=1&iN)i+;*stu_number=i;/重置学生记录个数if(feof(fp)fclose(fp);else printf(文件读错误);fclose(
5、fp);return;2学生信息的保存函数void SaveStu(struct student stu,int count,int flag)FILE*fp;int i;if(fp=flag?fopen(list.dat,ab):fopen(list.dat,wb)=NULL)printf(不能打开文件n);return;for(i=0;icount;i+)if(fwrite(&stui,sizeof(struct student),1,fp)!=1)printf(文件写错误n);fclose(fp);3.增加学生记录函数void InputStu(struct student stu,in
6、t*stu_number)char ch=y;int count=0;while(ch=y)|(ch=Y)system(cls);printf(ntt 增加学生记录 n);printf(nntt 请输入学生信息n);printf(n学号:);scanf(%d,&stucount.no);printf(n姓名:);scanf(%s,&stucount.name);printf(n性别:);scanf(n%c,&stucount.sex);printf(n语文成绩:)scanf(%3d,&stucount.score0);printf(n数学成绩:);scanf(%3d,&stucount.sco
7、re1);printf(n英语成绩:);scanf(%3d,&stucount.score2);stucount.sum=stucount.score0+stucount.score1+stucount.score2;stucount.average=stucount.sum/3.0;printf(nntt 是否输入下一个学生信息?(y/n);scanf(n%c,&ch);count+;*stu_number=*stu_number+count;SaveStu(stu,count,1);/参数1表示以追加方式写入文件 return;程序说明:(1)在程序第一次执行时,应首先进行学生信息的添加,
8、即调用InputStu()函数添加学生记录,并将录入的学生信息数据和统计的学生人数通过调用SaveStu()函数保存到list.dat文件中。浏览、删除等函数执行时均调用LoadStu()函数读取学生信息数据,处理完后,若学生信息发生了改变,则重新写入文件。为区分是添加信息还是重新保存,在SaveStu()函数的形参中,构造了一个flag标志变量,若flag=1,则添加信息并追加到数据文件中;若flag=0,则将学生的信息重新以覆盖方式保存到数据文件中。学完本章后,学生应该将SaveStu()函数的内容补充到相应的函数中去。相关的内容见附件的完整程序。(2)在LoadStu()函数中,要将学生
9、信息读入到结构体数组中,学生人数在InputStu()函数和DeleteStu()函数中会有改变。为了使数据更安全,在程序中没有使用全局变量来定义学生的人数,而是通过指针变量stu_number来存储学生的人数,使用指针变量是按地址传递数据,其目的是使形参(学生的人数)的变化影响实参(学生的人数)。五、要点总结 在学生信息管理系统中,使用文件的目的是将数据保存到磁盘中。在整个项目中,何时导入数据、何时要保存数据,需要全面考虑,函数中参数的作用及传递各不相同,这些内容要结合结构体及指针的相关知识来理解。10.2 理论知识理论知识 10.2.1 文件的基本概念一、文件的概念 “文件”是指存储在外部
10、介质上的数据的集合。数据是以文件的形式存放在外部介质上的,并通过文件名来识别。每个文件都有一个名称,程序可以通过文件操作存取数据,因此文件的输入/输出是文件最基本的操作。C语言把文件看作是一个字符(字节)的序列,即由一个个字符(字节)的数据顺序组成。文件可以从不同的角度进行分类。1根据数据的组织形式分类(1)文本文件 文本文件又称ASCII文件,文本文件的每一个字节存放一个ASCII码,代表一个字符。文本文件的输出与字符一一对应,一个字节代表一个字符。例如,10000占用5个字节,每个字节存放一位数字字符的ASCII码值。因此便于对字符进行逐个处理,也便于输出字符。文本文件由文本行组成,每一行
11、中可以有0个或多个字符,并以换行符n结尾,文本的结束是0 x1A。在向文本文件输出数据时,把换行符n转换成回车r和换行n两个字符。用文本文件向计算机输入时,将回车符和换行符n和r转换成一个换行符n。如输入:abcd efgh 存储在文本文件后(在这里用代表换行符n),输入时的换行符转换成回车和换行两个字符存放,于是第7个字符是e而不是f。(2)二进制文件 二进制文件是把内存中的数据按其在内存中的存储形式原样输出到磁盘上存放,一个字节并不对应一个字符,不能直接输出字符形式。例如,在基于16位的编译系统中(如Turbo C 2.0),整数10000在内存中占两个字节,存放在二进制文件中也占两个字节
12、,这两个字节存放它的二进制形式。二进制文件不像文本文件那样从回车换行符到换行符之间转换。2根据读写方式分类(1)顺序文件 顺序文件是指写入、存储与读出的顺序完全一致的文件。对顺序文件进行读写操作时,必须从文件头开始顺序进行,中间不能跳过任何一个数据。文件打开后,只能进行一种操作,要么读,要么写。(2)随机文件 随机文件是指可以直接对任意位置的元素进行读写的文件。对随机文件的读写操作,可以从文件中任何位置上的数据开始。文件打开后,既可读,又可写。另外,依照文件存放的介质,有卡片文件、纸带文件、磁带文件、磁盘文件等;依照文件的内容,有源程序文件、目标文件、数据文件等。由前所述,一个C文件是一个字节
13、流或二进制流。在C语言中对文件的存取是以字符(字节)为单位的,输入输出数据流的开始和结束仅受程序控制而不受物理符号(如回车换行符)控制。因此,这种文件又称为流式文件。二、文件操作过程在C语言中,对文件的操作有以下三个步骤:1.建立或打开文件。2.从文件中读取数据或向文件中写入数据。3.关闭文件。打开文件是将指定文件与程序联系起来,为文件的读写操作作好准备。从文件中读取数据,就是从指定文件中取数据,存入程序在内存的数据区域中,如变量或数组。向文件中写数据,就是将程序的输出结果存入指定的文件中,即文件名所对应的外存储器上的存储区中。关闭文件是取消程序与指定文件之间的联系,表示文件操作结束。三、文件
14、指针 在C语言中,对文件的访问是通过文件指针来实现的。在C语言中用一个指针变量指向一个文件,这个指针称为文件指针。通过文件指针就可以对它所指向的文件进行各种操作。定义文件指针的一般形式为:FILE*指针变量标识符;其中“FILE”应为大写,它实际上是由系统定义的一个结构,该结构中包含文件名、文件状态和文件当前位置等信息。用户不必关心FILE结构的细节。例如:FILE *fp;表示fp是指向FILE结构的指针变量,通过fp即可找到存放某个文件信息的结构变量,然后按结构变量提供的信息找到该文件,实现对文件的操作。习惯上把fp称为指向一个文件的指针。10.2.2 文件的打开和关闭 文件处理就是对文件
15、进行读写操作,在对文件读写之前必须先“打开”该文件,读写后一定要“关闭”该文件。一、文件的打开(fopen()函数)所谓“打开”,是指在程序和操作系统之间建立起联系,程序把所要操作的文件的一些信息通知给操作系统。打开文件的一般形式如下:FILE *fp;fp=fopen(filename,mode);其中,“fp”是一个文件指针;“filename”是一个DOS文件名;“mode”指出打开该文件的模式。fopen()函数的功能是以mode指定的模式打开filename指定的文件。调用fopen()函数有一个返回值,它是一个地址量,表示被打开文件信息区的起始地址。把这一返回值赋给文件指针fp之后
16、,即可通过该文件指针对文件进行操作。若指定的打开操作失败(例如用“r”方式打开一个不存在的文件),则函数返回一个NULL指针(即地址值为0,它是一个无效的指向)。mode取值及含义如表10-1所示。mode处理方式指定文件不存在时指定文件存在时rwarbwbabr+w+a+rb+wb+ab+读取(文本文件)写入(文本文件)追加(文本文件)读取(二进制文件)写入(二进制文件)追加(二进制文件)读取/写入(文本文件)写入/读取(文本文件)读取/追加(文本文件)读取/写入(二进制文件)写入/读取(二进制文件)读取/追加(二进制文件)出错建立新文件建立新文件出错建立新文件建立新文件出错建立新文件建立新文件出错建立新文件建立新文件正常打开文件原有内容丢失在文件原有内容末尾追加正常打开文件原有内容丢失在文件原有内容末尾追加正常打开文件原有内容丢失在文件原有内容末尾追加正常打开文件原有内容丢失在文件原有内容末尾追加例如:fp=fopen(file1,r);它表示打开名字为file1的文件,使用文件的方式为“只读”,并把返回的指向文件file1的指针赋给fp。这样,fp就和file1相联系了,或者说,