本节视频地址:点击这里
在我们先前的程序中,所处理的数据或者在代码中预先指定,或者通过命令行窗口手动输入。事实上这两种方法都是效率不高的方法,相对更常用的是通过文件读写操作处理以文件形式保存的数据。C语言中的文件操作主要包括文件数据的读、写和定位等辅助操作。
1、文件
C语言中定义了FILE这一结构用于文件操作,这一结构定义了表示文件的数据流以及用于控制文件操作的信息,如指向文件某个位置的标识、指向缓存区的指针和表示文件的状态信息等。在我们自己的源代码中定义一个FILE类型的实体通常没有意义。通常,实际操作FILE类型的实例仅在C语言提供的库函数内部完成,在编程过程中只需要定义一个FILE类型的指针变量,并对这个指针变量进行各种操作。例如我们需要输入输出两个文件,那么可以定义两个空的文件指针供后面使用:
FILE *pFin = NULL, *pFout = NULL;
2、打开文件
对一个文件进行操作,必不可少的两个因素就是文件的完整路径和操作方式,而对文件进行操作使用的是指向文件的指针,那么一个极为关键的问题就是如何将路径和操作方式同文件指针联系起来。在C语言中,实现这一方法的途径是通过打开文件函数。打开文件函数的原型为:
FILE * fopen ( const char * filename, const char * mode );
该函数的第一个参数传入的是目标文件的路径,第二个参数用于表示文件的操作模式,可以代表读/写、文本/二进制、刷新/续写等不同的模式。各种模式的含义有:
“r” |
读文本 |
“rb” |
读二进制 |
“r+” |
更新读文本 |
“rb+” |
更新读二进制 |
“w” |
写文本 |
“wb” |
写二进制 |
“w+” |
更新写文本 |
“wb+” |
更新写二进制 |
“a” |
续写文本 |
“ab” |
续写二进制 |
“a+” |
更新续写文本 |
“ab+” |
更新续二进制 |
使用fopen函数打开一个文件并进行操作完成后,需要将这个文件进行关闭。关闭文件使用fclose函数完成。fclose函数原型为:
int fclose ( FILE * stream );
该函数只有一个参数,即打开文件的指针。
将一个文件打开、处理、最后关闭的例子如下:
int main() { FILE *pF = NULL; fopen(“file.bin”, “rb"); //文件处理 fclose(pF); pF = NULL; }
3、文本文件读写
文件数据的读写通常有两种常用的方式,其中一种是按照文本文件的方式读写文件中的ASCII码信息,另一种方式是按照二进制文件的方式读取文件本身所表示的比特流。如果希望进行文本文件的读写操作,则在打开文件时就需要指定文件按照文本方式读取或写入。C语言为文本方式读写数据定义了多种输入输出函数:
(1)getc和putc函数
getc和putc函数是最简单的文本读写函数,分别实现一个字符常量或变量的读取和写入操作。两个函数的声明分别如下:
int getc ( FILE * stream ); int putc ( int character, FILE * stream );
getc函数只有一个参数就是读取目标文件的指针;putc函数第一个参数表示将要被写入目标文件的文本字符的ASCII码,第二个参数表示目标文件的指针。在putc中,字符使用int表示,但是在向文件写入的过程中,这个参数会被转化为unsigned char型进行写入。当读取文本文件到结束时,getc函数将会返回一个EOF字符,该字符的ASCII值为-1这一非法值,表示文件已结束。
(2)fgets和fputs函数
这两个函数用于从文本文件中读取和写入字符串。声明分别如下:
char * fgets ( char * str, int num, FILE * stream ); int fputs ( const char * str, FILE * stream );
fgets函数调用时需要三个参数,第一个参数指向字符串的首地址,第二个参数表示读入字符串的最大长度,第三个参数表示打开文件的指针。需注意,当指定第二个参数num时,这个参数所表示的长度包括了字符串结尾的0,因此可以读入的最多的字符个数为num-1。如果在读出num-1个字符之前就遇到换行符或EOF,则在这里结束读入,并在末尾补上\0。当读取成功时,函数将字符串的缓存str作为返回值返回;如果读取失败,函数将返回一个空指针。
fputs函数用于将字符串输出到文本文件中,第一个参数表示指向字符串常量或变量的指针,第二个参数表示写入文件的指针。需注意的是,该函数写入字符串不会输出结尾的0或者换行符,如果连续调用fputs将会使多个字符串首尾相接无法分辨。因此使用时应注意手动添加'\n'等间隔符以示区分。
(3)fscanf和fprintf函数
同scanf和printf函数类似,fscanf和fprintf函数表示格式化输入和输出数据,只是输入输出的对象由控制台界面变为文本文件。两个函数的声明如下:
int fscanf ( FILE * stream, const char * format, ... ); int fprintf ( FILE * stream, const char * format, ... );
事实上,将两个函数的第一个参数,即指向打开文件的指针分别换成stdin和stdout,则他们的功能同scanf和printf将完全一样。因为stdin和stdout分别表示的就是代表终端键盘和终端屏幕的文件指针。
两个函数的调用方法同scanf与printf类似。
4、二进制文件读写
对于二进制形式的文件而言无所谓格式的概念,只有按照文件原始的0/1存储方式保存着一种方法。因此读取二进制文件也不存在文本文件中各种格式、换行等因素,只有读取/写入数据和判断文件末尾两种基本情况。C语言中定义二进制文件的读写函数分别为fread和fwrite,声明方式如下:
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream ); size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );
fread函数定义了4个参数,第一个参数ptr为指向接受读取进来的文件数据的缓存的指针,这部分缓存必须是预先成功分配好的足够大的内存空间,如果不是合法的可写入的内存或者空间不足,在读取时程序将出现崩溃;第二个参数size表示
每一个数据单元所占字节数,这个字节通常用于表示二进制文件中数据按多少字节作为一个单元;第三个参数count表示总共读取多少个数据单元,第二三个参数的乘积size × count就是从二进制文件中读取的总的字节数;第四个参数表示打开的二进制文件指针。函数的返回值为实际读取到缓存区中数据的长度。
fwrite函数的定义和使用方式同fread类似,只是作用是以二进制的方式向文件中写入数据。
需要注意的是,以二进制方式读取文件时,读取的数据同文本读取时获得的ASCII码不同。二进制读取的数据的范围可以非常广,不局限于0~255之间,因此用-1代表文件结束的EOF便不再适用。在二进制文件读写中判断文件结束应使用feof函数。当函数feof(pF)返回值为1时,表示文件已经遇到文件结束,否则该函数会返回0。
5、文件定位函数
在前面的文件读写函数中,无论读取的数据或长或短,下一次读写仍能够继续上一次结束的位置继续操作。这是由于文件结构中有“文件位置指针”这一功能。当使用fopen打开文件是,文件位置指针便指向了文件开头。每次进行读写操作,程序都会对文件位置指针处的数据进行读写。操作完成后,文件位置指针会根据读写的数据长度移动到新的位置等待下一次操作。这一切都是系统自动完成,不需要程序员进行任何干预。
然而有时候,我们希望人为地对文件位置指针进行修改。为了实现这样的功能,C语言提供了文件定位函数,主要有fseek、ftell和rewind等。
(1)fseek函数
fseek函数用于将文件位置指针移动到指定的位置,该函数的声明为:
int fseek ( FILE * stream, long int offset, int origin );
该函数的第一个参数表示打开文件的指针,第二个参数表示相对于origin的偏移量(在文本文件中,该值或为0或为ftell返回的一个值),第三个参数表示文件位置指针的参考位置。不同的origin的设置代表文件相对不同位置的偏移:
Origin取值 |
SEEK_SET |
SEEK_CUR |
SEEK_END |
参考位置 |
文件头 |
当前位置 |
文件末尾 |
当文件指针pF指向一个二进制文件,则调用下面的语句使文件位置指针移动到距离文件头30字节的位置:
fseek(pF, 30L, SEEK_SET);
调用下面的语句使文件指针移动到距离文件末尾15个int型数据长度的位置:
fseek(pF, 15L * sizeof(int), SEEK_END);
如果文件指针指向一个文本文件,下面的语句使文件位置指针移动到文件的开头和结尾:
fseek(pF, 0L, SEEK_SET); fseek(pF, 0L, SEEK_END);
(2)ftell函数
该函数用于获取当前指针距离文件开头的距离,声明如下:
long int ftell ( FILE * stream );
该函数只有一个参数就是文件的指针。它的返回值代表了当前位置距离文件开始有多少个字节。
(3)rewind函数
该函数的作用在于将文件位置指针重置与文件开头,声明如下:
void rewind ( FILE * stream );
该函数只有一个参数即文件指针,没有返回值。它的作用同下面fseek的用法类似:
fseek(pF, 0L, SEEK_SET);