写在前面的题外话:
程序中处理的数据以及程序计算的结果都是通过输入输出实现的。程序的执行离不开输入和输出。程序通过输入输出设备与外界进行交互:需要赋值时输入设备将数据送入变量;需要查看变量运行结果时输出设备将结果显示在输出设备上。输入到变量中,以及经过计算保存到变量中的数据只能保存到程序结束,而且保存在内存中的数据只能保存到关机为止。如果要想持续地、长久地保存数据,就必须借助于外存储设备,比如磁盘、光盘等。
程序设计语言均提供访问和使用外存储设备的功能。外存储的信息是以文件的形式保存的,程序运行中与外存储器打交道,也是通过访问和使用文件信息实现的。
文件的概念
(1)文件
文件是程序设计中的一个重要概念。所谓文件就是按照一定的组织方式以一个名字存放在外存储设备上的数据的集合,操作系统是以文件为单位对数据进行管理。文件的名字一般包括主名和扩展名两部分,扩展名一般是文件类型的标志。
通常情况下文件驻留在外存储设备上,在使用时才调入到内存中。
(2)文件的分类
从不同的角度,将文件所分的类型不一致。按照读写顺序不同分为顺序文件和随机文件;按照文件的存储格式不同可以分为文本文件(以ASCII码形式保存)和二进制文件(以二进制格式保存)。
文本文件的每一个字符存储一个字符的ASCII码,因此它便于字符处理,比如字符的输出,但是它占用的磁盘空间较多。二进制文件的每一个字节存储的是二进制数,因此若要保存二进制文件的内容,就是将内存中的存储形式原样存储到磁盘上。这样的信息无法直接输出字符的形式,但是可以节省存储空间。
(3)文件流
C语言标准库文件的输入输出采用流的概念。文件是输入输出的对象,与文件交换信息建立与输入输出的关系就是流。C语言采用字符流和二进制流对数据文件进行存取:从一个文件输入,程序需要建立与该文件关联的输入流;要向一个文件输出,则要建立与之关联的输出流。同时也可以建立即可以输入也可以输出的流。创建流的动作被称为打开文件,文件打开后就可以进行读、写操作了。当处理完后不需要文件时,就可以撤销流,即关闭文件了。
打开、关闭、读、写都是对文件的操作。
文件类型指针
C语言使用文件,是在内存中开辟一个文件结构体,文件结构体保留了文件的有关信息,可以通过指向文件结构体的指针实现对文件的访问。
文件结构体指针的定义格式如下:
FILE *指针类型标准符 ;
其中,FILE是标准输入输出定义的用于处理文件的结构体类型。用FILE类型定义文件指针,一个文件指针只可以指向一个文件,n个文件要用n个文件指针。
对文件的操作均是通过函数实现的,这些函数的使用需要包含“stdio.h”文件。
文本文件的每一个字符存储一个字符的ASCII码,因此它便于字符处理,比如字符的输出,但是它占用的磁盘空间较多。二进制文件的每一个字节存储的是二进制数,因此若要保存二进制文件的内容,就是将内存中的存储形式原样存储到磁盘上。这样的信息无法直接输出字符的形式,但是可以节省存储空间。整数10000在内存中的形式见图1:
3)C语言对文件的处理方法
①缓冲文件系统:系统自动地在内存区为每一个正在使用的文件开辟一个缓冲区。用缓冲文件系统进行的输入输出又称为高级磁盘输入输出。
②非缓冲文件系统:系统不自动开辟确定大小的缓冲区,而由程序为每个文件设定缓冲区。用非缓冲文件系统进行的输入输出又称为低级输入输出系统。
标准C在stdio.h文件中有以下的文件类型声明:
typedef struct
{ shortlevel; /*缓冲区“满”或“空”的程度*/
unsignedflags; /*文件状态标志*/
charfd; /*文件描述符*/
unsignedcharhold; /*如无缓冲区不读取字符*/
shortbsize; /*缓冲区的大小*/
unsignedchar*buffer;/*数据缓冲区的位置*/
unsignedar*curp;/*指针,当前的指向*/
unsignedistemp;/*临时文件,指示器*/
shorttoken;/*用于有效性检查*/}FILE;
文件的基本操作包括打开、关闭、读、写、添加等操作。
图1 文件的读写示意图
所谓文件读取:就是从磁盘文件将数据读取到内存,因此对内存来说是输入;
文件写入:就是将内存中的数据输出到磁盘文件中,对内存来说是输出。
明白文件的读取操作对后续文件的操作就比较容易理解和记忆了。
文件的打开与关闭
文件在进行读、写操作之前要先打开,使用完要关闭。
(1)打开文件
在C语言中打开文件的操作通过标准库函数fopen完成,它返回一个FILE指针值。具体使用格式如下:
FILE *fopen(文件名,打开方式)
功能:按照指定的方式打开一个文件。
说明:
1)文件名是字符串常量、字符数组名等,用于代表文件名的量。文件名中应包含路径;
2)打开方式也是一个字符串,用于表示打开文件的读写操作,以及打开文件的存取格式。具体打开方式的见表1;
表1 文件打开方式说明
3)如果不能实现打开,则返回一个空指针。
(3)关闭文件
当一个文件使用完应该关闭,以免它被误用造成文件数据丢失等错误。用fclose函数关闭文件,其格式如下:
fclose(文件指针);
关闭文件就是让文件指针变量不指向该文件。
文件数据的读写
在C语言中对文件的读写操作是按照不同类型的文件进行的。主要分为ASCII码文件和二进制文件的读写操作。
(1)ASCII码文件的读写
1)字符读写函数
字符读写函数是以字符为单位的读写函数。每次可以从文件中读出或是写入一个字符,字符读写函数处理的文件格式是文本文件。
①字符写函数fputc
使用格式如下:
fputc(char ch,FILE *fp);
说明:格式中的字符可以是字符常量、变量或表达式
功能:将一字节的字符常量或变量写入文件指针所指向的文件中。如果输出成功,则返回该字符的ASCII码,失败,返回EOF。写入函数用于创建文件或是向文件中添加数据,是将内存中变量的值通过输出流存入到外部文件中的过程。
②字符读取函数fgetc
使用格式如下:
char ch=fgetc(FILE);
功能:从文件指针指定的文件中读入一字节的字符赋给赋值号左边的字符变量。如果成功,则返回该字符的ASCII码,如果文件结束,则返回EOF。写入函数用于将外部文件中保存的数据,通过输入流输入并赋值给内存变量。
2)字符串读写函数
①字符串写入函数
字符串写入函数fputs函数实现向一个指定文件中写入一个字符串,其使用格式如下:
fputs(char *str , FILE *fp);
说明:str可以是一切表示字符串形式的数据,包括字符串常量、字符数组名或是字符指针。
②字符串读取函数
字符串读取函数fgets函数实现从指定的文件中读取一个字符串存入到字符数组中,使用格式如下:
fgets(char *str ,int n,FILE *fp);
说明:格式中str应为字符数组或字符指针变量;n表示从文件中读出的字符串的个数,应不超过n-1个字符,读入最后一个字符后,会在末尾加字符串结束的标志’\0’。
3)格式化读写函数
格式化读写函数分别由fscanf函数和fprintf函数实现。fscanf函数与fprintf函数与之前使用的scanf函数和printf函数的功能相似,都是按照格式化形式读写函数,所不同的是前两者读写的对象时磁盘文件,而后两者是输入输出的终端设备。
这两个函数的使用格式如下:
fscanf(FILE *fp ,char *format,变量的地址表);
fprintf(FILE *fp , char *format ,输出项表);
说明:这两个函数的参数,除了增加了一个指向文件的文件指针变量后,其它参数与scanf函数及printf函数一样。
(2)二进制文件读写——直接读写函数
前面三类的读写函数,都需要将操作的数据做数据形式转换,即读取时,将磁盘上保存的文本格式的数据,转换为二进制形式数据存入内存变量;输出时将内存中保存的二进制形式数据转换为ASCII码形式。转换需要时间,如果产生的输出是给人看的,这种转换是必需的,但如果把数据存入文件中的目的只是为了以后的读取使用,那么这种转换就没有必要了。同时数据转换可能会引起数据的丢失,尤其是对于实数类型数据,转换来转换去可能会产生误差。为了解决此类问题,C语言的标准库提供了直接读写的二进制流的文件读写函数fread函数和fwrite函数。
这两个函数可用来读写一组数据,比如一个数组、一个结构体变量的值等“一块”数据,其格式如下:
fread(void *buffer ,unsigned size ,unsigned count , FILE *fp);
fwrite(void *buffer ,unsigned size ,unsigned count , FILE *fp);
说明:
1)参数buffer是一个指针,即地址量。对fread函数是要存放输入数据的首地址;对fwrite函数是要存放输出数据的首地址;
2)参数size是一个长度值,即要读写的数据块的字节数;
3)参数count表示读写多少个长度为size的数据块;
4)参数fp是实现读写操作的文件指针;
5)如果fread函数 和 fwrite函数调用成功,返回count的值,即数据项的个数。如果fwrite函数返回值小于count,则说明不成功;如果fread函数返回值小于count,也可能是由于文件中所剩余的数据不足,因此判断二进制流是否读取结束,应该用函数feof函数判断。