前言
C语言的文件功能允许将数据独立于程序之外,单独以磁盘文件的形式存储起来,当程序需要时,即可通过一定的方式访问这些文件。存储在磁盘上的数据文件,既可以是程序所需的源数据文件,也可以是程序运行后的结果数据文件。显然,C语言的文件功能为方便批量数据输入以及保存程序运行结果提供了支持。
C语言的文件功能是通过相应的文件操作函数实现的,本节将系统介绍相关知识,包括文件的概念、文件操作的基本方法和相关函数,并结合具体实例介绍文件应用程序的设计方法。
目录
16.1 文件概述
16.1.1 文件的概念
- 文件:存储在外部介质上的信息的集合。
- 程序文件:存储程序代码的文件。
- 数据文件:存储数据的文件。
- 设备文件:所有的外部设备均被作为文件对待,这种文件称为设备文件。对外部设备的输人输出处理就是读写设备文件的过程。
- 输出文件缓冲区:当向文件输出数据时,准备输出的数据先写入文件缓冲区,等该区填满后再输出到文件中。这一过程称为“写文件”,是数据输出过程。
- 输入文件缓冲区:当从文件中输入数据时,也是把读入的数据先写入文件缓冲区,等文件缓冲区数据装满之后再整个送给程序。这一过程称为“读文件”,是数据输入过程。
- 文件缓冲区是连接计算机内存数据与外存文件的桥梁。
- 计算机对文件的操作总体上分成输入和输出两大类,对文件的输人输出(I/0)过程是通过操作系统进行管理的。
16.1.2 文件的分类
按照数据在文件中的存储方式,C语言把文件分为文本文件和二进制文件两类:
文本文件:以ASCII码字符形式存储的文件称为文本文件,又叫ASCII文件。例如用高级程序语言编写的程序文件是文本文件。
二进制文件:是文件的另一种存储形式,将内存中的数据存入磁盘的时候不需要进行数据转换,它以二进制数据方式存入磁盘。例如C程序的目标文件(扩展名为.obj)和可执行文件(扩展名为exe)都是二进制文件。现通过一个具体的例子说明文本文件和二进制文件的区别。有一个整数2501,如果是用二进制的形式表示,2501作为一个int型的常数在内存中存储只需2个字节,直接把十进制整数2501转换为二进制整数就可以了。如果用ASCII形式输出,由于这里涉及到2、 5、0、1这四个数字,存储时就要4个字节,每个数字都用ASCII值来表示。整数2501的这两种存储形式如下图所示。
16.1.3 文件的一般操作过程
使用文件的一般步骤是:打开文件→操作文件→关闭文件
打开文件:建立用户程序与文件的联系,系统为文件开辟文件缓冲区。
操作文件:是指对文件的读、写、追加和定位操作。
读操作:从文件中读出数据,即将文件中的数据输入到计算机内存。
写操作:向文件中写入数据,即将计算机内存中的数据输出到文件。
追加操作:将新的数据写到文件原有数据的后面。
定位操作:移动文件读写位置指针。关闭文件:切断文件与程序的联系,将文件缓冲区的内容写入磁盘,并释放文件缓冲区。
16.1.4 文件类型指针
在C语言中,凡是要对已打开文件进行操作,都要通过指向该文件结构的指针。因此,需要在程序中定义FILE型(文件型)的指针变量,并使其指向要操作的文件。
文件型指针变量的定义形式如下:
FILE *变量名;
例如,使用如下语句将定义一个文件结构类型的指针变量p:FILE *p;当程序同时处理多个文件时,则需定义多个FILE类型的指针变量,以保证每个打开的文件都有独立的文件指针指向它。
数据类型“FILE”在Turbo C 2.0中的原型定义:
typedef struct { short level; /* fill/empty level of buffer */ unsigned flags; /* File status flags */ char fd; /* File descriptor */ unsigned char hold; /* Ungetc char if no buffer */ short bsize; /* Buffer size */ unsigned char *buffer; /* Data transfer buffer */ unsigned char *curp; /* Current active pointer */ unsigned istemp; /* Temporary file indicator */ short token; /* Used for validity checking */ }FILE; /* This is the FILE object */
16.2 文件的基本操作
16.2.1 打开和关闭文件
- 文件打开函数fopen()
fopen()函数用来实现打开文件,它解决如下3个问题:
⑴ 指定要打开的文件名;
⑵ 指定文件的使用方式,如是读文件还是写文件等;
⑶ 为打开的文件指定一个指针变量,以便使用这个指针变量对文件进行访问。fopen()函数的调用方式通常为:
FILE *fp;
fp=fopen(文件名, 使用文件的方式);例如:
fp= fopen("example.txt", "r");//它表示要打开的文件名为example.c,使用文件的方式是“r”方式,指向文件的指针是fp。在程序中使用fopen()打开文件时,通常要检查文件打开的正确性,以便决定程序是否继续向下执行。例如:
if ((fp = fopen("example.txt", "r"))= =NULL)
{
printf ("cannot open this file\n ");
exit(1);
}
其中exit()函数的作用停止程序执行,使控制返回操作系统。通常返回值为0时表示正常返回,非0表示非正常返回。
- 关闭文件函数fclose()
fclose()函数的一般格式如下:
fclose(文件指针);
该函数实现的功能就是关闭“文件指针”所指向的文件,释放打开文件时使用的结构体变量,断开文件指针与文件的联系。如果输出缓存中还有数据,则写入磁盘;如果输入缓存中还有数据,则丢弃。如果操作成功,则其返回值为0,否则,返回值为EOF。“EOF”是在头文件stdio.h中定义的符号常量,其值为-1。例如,我们在前面曾用fopen()函数打开example.txt文件,在打开时指定fp指针指向该文件,因此可使用如下语句将example.txt文件关闭:fclose(fp);
16.2.2 最基本的文件读写函数
- fputc( )函数
一般形式如下:
fputc(ch,fp);
其中:ch是要输出的字符,fp是文件指针变量。fputc()函数的作用是将字符(ch的值)输出到fp所指向的文件中,即向指定文件中写入一个字符。
如果要向终端输出一个字符,则使用如下形式:
fputc(ch,stdout);//stdout是标准输出终端设备的文件指针,由系统定义。程序示例:
(1)使用fputc()函数在屏幕显示一个字符串。include "stdio.h" main() { char *p="This is a example."; while(*p!='\0') fputc(*p++,stdout); }
(2)把从键盘输入的一个字符串写入到磁盘文件example.txt中。
#include "stdio.h" main() { char ch; FILE *fp; fp=fopen("example.txt","w"); printf("Enter a string: "); while((ch=getchar())!='\n') fputc(ch,fp); fclose(fp); }
- fgetc( )函数
fgetc()函数的功能是从指定文件读入一个字符,该文件必须是以读或读写方式打开的。
通常使用如下的形式调用fgetc()函数:ch=fgetc(fp);
其中,fp为文件型指针变量,ch为字符变量。正常情况下fgetc()函数的返回值是从文件中读出的一个字符。程序示例:
把一个文本文件的内容复制到另一个文本文件中。#include"stdio.h" main() { char ch,source[10],target[10]; FILE *fp_s,*fp_t; printf("Enter the source filename: "); scanf("%s",source); printf("Enter the target filename: "); scanf("%s",target); if((fp_s=fopen(source,"r"))==NULL) { printf("cannot open source file.\n"); exit(1); } if((fp_t=fopen(target,"w"))==NULL) { printf("cannot open target file.\n"); exit(1); } while(!feof(fp_s)) fputc(fgetc(fp_s),fp_t); fclose(fp_s); fclose(fp_t); }
16.2.3 文件的数据块读写操作
文件的数据块读写是指对文件进行读写操作时,一次读写多个字节的数据,C语言提供的操作函数是fread()函数和fwrite()函数。fread()函数从文件中读取一个数据块,fwrite()函数将一个数据块写到文件中。如果文件以二进制形式打开,用fread()和fwrite()函数就可以读写任何类型的信息。
- fread()函数
fread()函数把指定文件中的一个数据块读到内存中,它的一般调用形式如下:
fread(buffer,size,count,fp);
其中:fp是读取数据的文件指针;buffer是接受文件数据的内存首地址,通常是指针变量名、数组名等;size是一个数据块的字节数(即数据块的大小);count是执行一次fread()函数读取的数据块的数目。例如,设string.txt是一个文本文件,s是长度为60的char型一维数组,则执行如下语句后,将读出string.txt文件中的前10个字符,并依次存储到数组s的前10个元素中:
FILE *fp;
fp=fopen("string.txt","r");
fread(s,2,5,fp);
fwrite()函数
fwrite()函数的功能是把内存中的一些数据块写到指定的文件中,它的一般调用形式如下:
fwrite(buffer,size,count,fp);
其中:fp是读取数据的文件指针;buffer是数据块的内存首地址,通常是指针变量名、数组名等;size是一个数据块的字节数(即数据块的大小);count是执行一次fwrite()函数从内存输出到fp文件的数据块数目。例如,下面的语句将把内存中结构体数组stud的数据输出到fp指向的文件中:
for(i=0; i<40; i++)
fwrite(&stud[i], sizeof(struct student_type), 1, fp);
当然,也可以使用下面的语句,一次就把40个数据输出到文件中:
fwrite(stud, sizeof(struct student_type), 40, fp);
16.3 文件的其他操作
16.3.1 文件的格式化读写
在C语言中,通用的文件格式化读写函数是fscanf()和fprintf()。fscanf()函数实现文件的格式化读操作,即文件的格式化输入;fprintf()函数实现文件的格式化写操作,即文件的格式化输出。
- 文件的格式化读操作
文件的格式化读操作由fscanf()函数实现,它的功能是从指定的文件中,按照说明的格式向变量提供数据。一般使用格式如下:
fscanf(fp,格式字符串,输入表列);
其中:fp是文件指针,“格式字符串”和“输入表列”与scanf()函数的相关内容相同。例如,用以下fscanf()函数可以从磁盘文件上读入ASCⅡ字符:
fscanf(fp,"%d,%f",&i,&t);//该语句将磁盘文件中的数据送给变量i、t,其中的磁盘文件由fp指向。
- 文件的格式化写操作
文件的格式化写操作由fprintf()函数实现,它的功能是将指定变量的值,按照一定的格式写到指定的文件中。一般使用格式如下:
fprintf(fp,格式字符串,输出表列);例如:
fprintf(fp,"%d,%6.2f",i,t);//它的作用是将整型变量i和实型变量t的值按%d和%6.2f的格式输出到fp指向的文件中。
- 程序示例
从键盘输入一个字符串和一个十进制整数,将它们写入test文件中,然后再从test文件中读出并显示在屏幕上。
#include "stdio.h" main() { char s[100]; int a; FILE *fp; if((fp=fopen("test", "w"))==NULL) { printf("Cannot open file.\n"); return; } fscanf(stdin,"%s%d",s,&a); fprintf(fp,"%s %d",s,a); fclose(fp); if((fp=fopen("test","r"))==NULL) { printf("Cannot open file.\n"); return; } fscanf(fp,"%s%d",s,&a); fprintf(stdout,"%s %d\n",s,a); fclose(fp); }
16.3.2 文件的随机读/写操作
要实现文件的随机读写,必须首先实现对文件指针的随机定位,即强制将文件的指针指向用户所希望的位置。C语言对文件的定位主要使用如下三个函数:fseek()、rewind()和ftell()。
- fseek()函数
fseek()函数的功能是改变文件位置指针,其调用形式如下:
fseek(fp,offset,position);
- rewind()函数
rewind()函数的作用是将文件位置指针复位,其调用形式如下:
rewind(fp);
执行rewind()函数后,对于fp指向的文件,不管当前的文件位置指针在何处,都使它复位到文件的开始位置。
- ftell()函数
ftell()函数用于获取文件位置指针,其调用形式如下:
ftell(fp);
ftell()函数的返回值是fp所指向文件的当前读写位置,该值是一个长整型数,是位置指针从文件开始处到当前位置的位移量的字节数。
16.3.3 文件的字符串操作
- fgets()函数
fgets()函数的一般调用形式:
fgets(buffer,n,fp);
其作用是从fp指向的文件中读取n-1个字符,然后存储到以buffer为首地址的的内存空间中。
- fputs()函数
fputs()函数的一般调用形式:
fputs(buffer,fp);
其作用是将内存buffer中的字符串写到fp指向的文件中,buffer可以是一个字符串常量,也可以是字符串的首地址。
- 程序示例
将字符串“Visual C++”和“Visual basic”依次存入文件text中,然后将第一个字符串读出并显示出来。
#include "stdio.h" main() { FILE *fp; char string[20]; fp=fopen("text","w+"); fputs("Visual C++\n",fp); fputs("Visual basic\n",fp); rewind(fp); fgets(string,20,fp); puts(string); fclose(fp); }