1、第十章 文 件,10.1 文件 10.2 缓冲文件系统基础,10.1 文 件,10.1.1 文件的概念,磁盘文件在DOS管理中被定义为存贮在外部介质上的程序或数据的集合, 是一批逻辑上有联系的数据每个文件都有一个文件名作为标识,每个文件在磁盘中的具体存放位置、格式都由操作系统中的文件系统管理, 也就是说,操作系统是以文件为单位对程序或数据进行管理的。编辑后存于磁盘上的源程序文件*.C,经编译后得到的目标文件*.OBJ,连接之后形成的可执行文件*.EXE等。,在C语言中文件的含义更为广泛,不仅包含以上所述的磁盘文件,还包括一切能进行输入/输出的终端设备,它们被看成是设备文件。如键盘常称为标准输入
2、文件,显示器称为标准输出文件。 文件是由磁盘文件和设备文件组成的。作为磁盘文件之一的数据文件是本章学习的主要对象。数据文件可以看作是C中最后一种数据类型, 是C语言重要的组成部分。根据文件内数据的组织形式,文件可分为文本(text)文件和二进制文件。文本文件又称为ASCII码文件,它的每一个字节存放一个字符的ASCII码。,10.1.2 数据流,数据流是对数据输入输出行为的一种抽象。各种各样的终端设备或磁盘文件的细节是非常复杂多样的直接对它们编程将会非常繁琐。引入数据流的概念有效地解决了这一难题。只要建立了输入输出数据流,编程者在应用程序中就不需要关心底层输入输出设备或是任何磁盘文件的具体细节
3、差异。程序中要输入数据,只需从输入数据流中读入;输出数据只需向输出数据流中写出即可,这样就使程序完全与具体硬件资源脱离了关系,也就是说数据流使C程序与具体系统完全不相关,使C程序可以非常方便地移植。,10.1.3 C的文件系统及其与流的关系,C的文件系统可分为缓冲文件系统和非缓冲文件系统两类。所谓缓冲文件系统,又称高级磁盘输入输出系统。在调用这种文件处理函数时,会自动在用户内存区中为每一个正在使用的文件划出一片存贮单元,称为开辟一个缓冲区。 设立缓冲区的原因是磁盘的读写速度比内存的处理速度要慢很多,而且磁盘驱动器是机电设备,定位精度比较差,所以磁盘数据存取要以扇区(磁盘上某磁道中的一个弧形段,
4、通常存放固定数量的数据)或者簇(由若干扇区组成)为单位。,10.2 缓冲文件系统基础,一般缓冲文件操作有三个必需的步骤: (1) 在使用文件前要调用打开函数将文件打开, 若打开失败,则返回一个空指针;若打开正常,可以得到一个文件指针,并利用它继续对文件操作。 (2) 可调用各种有关函数,利用该指针对文件进行具体处理,一般要对文件进行读或写操作。 (3) 在文件用完时,应及时调用关闭函数来关闭文件, 切断数据流,防止数据遗失或误操作破坏文件内容。,10.2.1 文件指针,文件类型FILE不是C语言的新类型,它是用typedef定义出来的有关文件信息的一种结构体类型。如Turbo C 2.0版的s
5、tdio.h文件中有如下的定义:,typedef struct short level; /* 缓冲区“满”或“空”的程度 */unsigned flags; /* 文件状态标志 */char fd; /* 文件描述符 */unsigned char hold; /* 如无缓冲区不读取字符 */,short bsize; /* 缓冲区的大小 */unsigned char *buffer; /* 数据缓冲区的位置 */unsigned char *curp; /* 当前工作指针 */unsigned istemp; /* 临时文件, 指示器 */short token; /* 用于有效性检查
6、*/ FILE;,10.2.2 打开文件(fopen函数),打开函数fopen() 的调用方式是: FILE *fp; fp=fopen(文件名, 使用文件方式); 例如, fp=fopen(“A1.DAT“, “r“);,表 12.1 文件使用方式,10.2.3 关闭文件(fclose函数),在使用完一个文件后应该调用fclose函数关闭文件。fclose函数的调用格式为fclose(文件指针)。例如fclose(fp);就把指针fp所指的文件关闭了,也就是断开了打开文件时建立的数据流fp与具体文件的联系,即不能再通过fp对某个具体文件进行操作。,如果在程序终止之前不关闭文件, 将可能丢失缓
7、冲区中最后一批未处理的数据,因为fclose函数的调用不仅释放文件指针,还刷新缓冲区。fclose函数将缓冲区中可能遗留的未装满送走的数据输入内存或输出至磁盘文件,以确保数据不丢失。当然,程序结束时会自动关闭文件,但用完文件后及时关闭是一个好的编程习惯。 fclose函数也返回一个值:0表示顺利返回,非0表示关闭错误。,10.2.4 文件的读写,1. fputc函数和fgetc函数fputc函数的调用形式为fputc(ch,fp); 该函数的作用是将字符(ch的值)输出到fp所指向的文件上去。其中ch是要输出的字符,它可以是一个字符常量,也可以是一个字符变量。fp是文件指针,它是从fopen函
8、数得到的返回值。fputc函数也带回一个值,如果输出成功则返回值就是输出的字符;如果输出失败,则返回一个EOF。 EOF是在stdio.h文件中定义的符号常量,值为-1。,fgetc函数的调用形式为ch=fgetc(fp); 该函数的作用是从指定文件读入一个字符, 该文件必须是以读或读写方式打开的。其中fp为文件型指针,指向所打开备读的文件;ch为字符变量,接收fgetc函数带回的字符如果在执行fgetc读字符时遇到文件结束符,函数则返回一个文件结束标志EOF, 可以利用它来判断是否读完了文件中的数据。如想从一个磁盘文件顺序读入字符并在屏幕上显示出来,可编程为 while(ch=fgetc(f
9、p)!KG-*4=EOF)putchar(ch);,EOF不是可输出字符,因此在屏幕上显示不出来。由于字符的ASCII码不可能出现-l,因此EOF定义为-l 是合适的。当读入的字符值等于-1(即EOF)时,表示读入的已不是正常的字符而是文件结束符。但以上只适用于读文本文件。现在标准C已允许用缓冲文件系统处理二进制文件,而读入某一个字节中的二进制数据的值有可能是-1,而这又恰好是EOF的值。 这就出现了读入有用数据却被处理为“文件结束”的情况,即终止符设置不恰当。为了解决这个问题,标准C提供了一个feof()函数来判断文件是否真的结束。feof(fp)用来测试fp所指向的文件当前状态是否为“文件
10、结束”,如果是文件结束,函数feof(fp) 的值为1(真),否则为0(假)。,例如,顺序读入一个二进制文件中的数据的程序段如下:,while(!feof(fp) c=fgetc(fp); 当未遇文件结束时,feof(fp)的值为0,!feof(fp)为l,读入一个字节的数据赋给变量c(接着可做其它处理),之后再求feof(fp)函数,循环工作直到文件结束,feof(fp)值变为1, !feof(fp) 值为0,结束while循环。这种方法也适用于文本文件。,例10.1 建立一个磁盘文件,将键入的回车前的若干个字符逐个写入该文件。,include main( ) FILE *fp; char
11、ch, filename13; printf(“nInput the files name: “); gets(filename); /* 注1 */if(fp=fopen(filename, “w“)=NULL)printf(“Can not open the filen“); exit(0); ,printf(“Input the characters to the file: n“); while(ch=getchar( ) ! = n) /* 注2 */ fputc(ch, fp); /* 注3 */putchar(ch); /* 注4 */fclose(fp); ,运行情况如下: I
12、nput the files name: fileex1.dat (注1要求的输入磁盘文件名)Input the characters to the file: What inside the file (注2 要求的键入一个字符串)What inside the file (注4输出到显示器上的字符串, 与写入文件的内容一样, 以资核对) 程序运行之后,可以查看文件目录,将多出一个名为fileex1.dat的数据文件,可用DOS命令将其内容打印出来: type fileex1.dat What inside the file (由注3行循环写入的),2. fgets函数和fputs函数 fg
13、ets的作用是从指定文件读入一个字符串。如: fgets(str, n, fp); 从fp指向的文件读入n-1个字符,并把它们放到字符数组str中,如果在读入n-1个字符结束之前遇到换行符或EOF, 读入即结束。字符串读入在最后加一个0字符, fgets函数返回值为str的首地址。 fputs函数的作用是向指定的文件输出一个字符串, 如:fputs(China, fp);,3. fprinf函数和fscanf函数fprintf函数、fscanf函数与printf和scanf函数作用类似, 都是格式化读写函数。前二者的读写对象是磁盘文件,而后二者是终端设备。所以前二者函数调用参数中要多出一代表文
14、件的文件指针。一般调用方式为 fprintf(文件指针, 控制字符串, 参量表); fscanf(文件指针, 控制字符串, 参量表);,例 10.2 按格式键入字符型、整型、实型各一数,写入文件dform.dat,再读出送显。,includemain() int i, i1; char ch, ch1; float f, f1; FILE *fp; printf(“nInput ch i f: “); scanf(“%c %d %f“, ,if(fp=fopen(“dform.dat“, “w“)=NULL) /* 注1*/printf(“Can not open the filen“); e
15、xit(0); fprintf(fp, “%c %5d %4.1f“, ch, i, f); /* 注2 */fclose(fp); /* 注3 */if(fp=fopen(“dform.dat“, “r“)=NULL)printf(“Can not open the filen“); exit(0); fscanf(fp, “%c %d %f“, ,程序运行情况: Input ch i f: a 2 2.2 (提示及输入)a 2 2.2 (屏幕显示)dform.dat中的内容同屏显。,4. fread函数和fwrite函数,它们的一般调用形式为 fread(buffer, size, cou
16、nt, fp); fwrite(buffer, size, count, fp); 其中: buffer 是一个地址。对fread来说,它是读入数据将要存放处的地址。对fwrite来说,是要输出数据的地址(以上指的是起始地址)。 size是要读写的一个数据块的字节数。 count是要进行读写数据块的个数。 fp是文件指针, 指向待读或写的文件。,例 10.3 建立一个有关工人工资的数据文件。,includedefine SIZE 6struct staff char name10; int salary; int cost; workerSIZE; void savef() FILE *fp;
17、 int i; if(fp=fopen(“work.dat“, “wb“)=NULL),printf(“Can not open the filen“); return; for(i=0; iSIZE; i+)if(fwrite( ,10.2.5 文件的定位,1. rewind函数rewind函数可以强制使当前工作指针指向文件的开头。一般在要重新从头读写文件时使用。如下例,在读了文件dfr.dat一遍送显示器后,文件的位置指针已移到文件的最后,为了重新读一遍再写到文件dfw.dat中,必须先执行一次rewind函数,才能正确读出。,例 10.4 将已建好的文件dfr.dat的内容顺序读一遍送显
18、示器, 再读一遍复制到文件dfw.dat中。,includemain( ) int i; char ch; float f, f1; FILE *fp1, *fp2; if(fp1=fopen(“dfr.dat“, “r“)=NULL) printf(“Can not open the file for readingn“); exit(0); ,if(fp2=fopen(“dfw.dat“, “w“)=NULL)printf(“Can not open the file for writingn“); exit(0); fscanf(fp1, “%c %d %f“, ,2. fseek函数利
19、用fseek函数可以控制文件位置的指针进行随机读写。 fseek函数的调用形式为 fseek(文件类型指针, 位移量, 起始点); 起始点用0、1 或2 代表, 0文件的开始, 1当前位置, 2文件末尾; 位移量指从起始点向前移动的字节数; fseek函数一般用于二进制文件, 因为文本文件要发生字符转换,计算位置时容易发生混乱。,例 10.5 将例10.3形成的职工数据文件中的第1,3,5个工人的信息读出、 送显。,include define SIZE 6 struct staff char name10; int salary; int cost; workerSIZE;,main() F
20、ILE *fp; int i; if(fp=fopen(“work.dat“, “rb“)=NULL)printf(“Can not open the filen“); exit(0); for(i=0; iSIZE; i+, i+)fseek(fp, i*sizeof(struct staff), 0); fread( ,若形成work.dat文件时的输入数据为 Li1 1100 100 Li2 1200 200 Li3 1300 300 Li4 1400 400 Li5 1500 500 Li6 1600 600 则此程序的运行结果为Li1 1100 100Li3 1300 300Li5 1500 500,3. ftell函数ftell函数的作用是得到流式文件中位置指针的当前位置, 用相对于文件开头的位移量来表示。 由于文件的位置指针经常移动,往往不易搞清其当前位置,用ftell()函数可以返回其当前位置,若返回-1L,表示函数调用出错。 例如, i=ftell(fp); if(i=-1L)printf(“errorn“); 。,