基础概念
数据流
程序与数据的交互是以流的形式进行的。进行C语言文件的存取时,都会先进行“打开文件”操作,这个操作就是在打开数据流,而“关闭文件”操作就是关闭数据流。
缓冲区
在程序执行时,所提供的额外内存,可用来暂时存放做准备执行的数据。它的设置是为了提高存取效率,因为内存的存取速度比磁盘驱动器快得多。
文件类型
分为文本文件和二进制文件两种。
文本文件是以字符编码的方式进行保存的。
二进制文件将内存中数据原封不至文件中,适用于非字符为主的数据。如果以记事本打开,只会看到一堆乱码。
其实,除了文本文件外,所有的数据都可以算是二进制文件。二进制文件的优点在于存取速度快,占用空间小,以及可随机存取数据。
文件存取方式
包括顺序存取方式和随机存取方式两种。
顺序读取也就是从上往下,一笔一笔读取文件的内容。保存数据时,将数据附加在文件的末尾。这种存取方式常用于文本文件,而被存取的文件则称为顺序文件。
随机存取方式多半以二进制文件为主。它会以一个完整的单位来进行数据的读取和写入,通常以结构为单位。
文件操作方式
C语言中主要通过标准I/O函数来对文本文件进行处理。相关的操作包括打开、读写、关闭与设置缓冲区。
相关的存取函数有:
fopen(), fclose(), fgetc(), fputc(), fgets(), fputs(), fprintf(), fscanf()等
打开文件
函数原型为:
_CRTIMP FILE * __cdecl fopen(const char *, const char *);
第一参数为文件名,第二个参数为打开模式。
打开成功,fopen返回一个结构指针地址,否则返回一个NULL。如果没有指定文件路径,则默认为当前工作目录。如:
FILE *fp; fp = fopen("c:\\temp\\test.txt", "r") //由于反斜杠\是控制字符,所以必须再加一个反斜杠
使用fopen()函数打开的文件会先将文件复制到缓冲区。注意:所下达的读取或写入动作,都是针对缓冲区进行存取而不是磁盘,只有当使用fclose()函数关闭文件时,缓冲区中的数据才会写入磁盘。
关闭文件
函数原型为:
_CRTIMP int __cdecl fclose(FILE *);
关闭成功返回值0,否则返回非零值。
注:在执行完文件的操作后,要进行“关闭文件”操作。虽然程序在结束前会自动关闭所有的打开文件,但文件打开过多会导致系统运行缓慢,这时就要自行手动关闭不再使用的文件,来提高系统整体的执行效率。
/* 文件读写演示 */ FILE *fp; fp = fopen("c:\\temp\\test.txt", "r");//读取文件指针 if(fp == NULL)//判断指针是否为空 printf("fail to open the file! \n"); else { printf("The file is open! \n"); fclose(fp);//关闭文件流 }
文本文件字符操作
读取字符
函数原型为:
_CRTIMP int __cdecl fgetc(FILE *);
字符读取函数fgetc()可从文件数据流中一次读取一个字符,然后读取光标移动到下一个字符,并逐步将文件的内容读出。
如果字符读取成功,则返回所读取的字符,否则返回EOF(end of file)。EOF是表示数据结尾的常量,真值为-1。
另外,要判断文件是否读取完毕,可利用feof()进行检查。未完返回0,已完返回非零值。
feof()函数原型为:
_CRTIMP int __cdecl feof(FILE *);
/* 读取字符演示 利用feof()函数检查文件是否读取完毕 */ FILE *fp; fp = fopen("c:\\path\\e.txt","r"); if(fp == NULL){ printf("文件打开失败"); }else{ while(!feof(fp)){ printf("%c",fget(fp)); } } fclose(fp);
/* 读取字符演示 利用EOF检查文件是否读取完毕 */ FILE *fp; fp = fopen("c:\\path\\e.txt","r"); if(fp == NULL){ printf("文件打开失败"); }else{ while((char ch = fgetc(fp))!=EOF){ printf("%c",ch); } } fclose(fp);
写入字符
函数原型为:
_CRTIMP int __cdecl fputc(int, FILE *);
/* 写入字符演示 */ char filename[20], ch; FILE *fp; printf("Enter a filename: "); scanf("%s", filename); printf("Enter some characters to output to file: "); if((fp = fopen(filename, "w")) == NULL) printf("fail to open! \n"); else { while((ch = getchar()) != '\0') fputc(ch, fp); } fclose(fp);
文本文件字符串操作
读取字符串
函数原型为:
_CRTIMP char * __cdecl fgets(char *, int, FILE *);
fgets函数的作用是从指定文件读入一个字符串,如:fgets(str, n, fp);
参数n为要求得到的字符个数,但只从fp指向的文件输入n-1个字符,然后在最后加一个’\0’字符,因此得到的字符串共有n个字符,把它们放在字符数组str中。如果在读完n-1个字符之前遇到换行符或EOF,读入结束。
写入字符串
函数原型为:
_CRTIMP int __cdecl fputs(const char *, FILE *);
fputs函数的作用是向指定文件输出一个字符串,如:fputs(“Hey”, fp);
把字符串”Hey”输出到fp指向的文件。fputs函数的第一个参数可以是字符串常量、字符数组名或字符型指针。若输出成功,则返回0,否则返回EOF。
格式化存取函数
函数原型为:
_CRTIMP int __cdecl fprintf(FILE *, const char *, ...); _CRTIMP int __cdecl fscanf(FILE *, const char *, ...);
它们与printf和scanf函数相仿,都是格式化读写函数。不同的是:fprintf和fscanf函数的读写对象不是终端(标准输入输出),而是磁盘文件。printf函数是将内容输出到终端(屏幕),因此,fprintf就是将内容输出到磁盘文件了。
/* 格式化操作演示 */ FILE *fp; int num = 10; char name[10] = "Leeming"; char gender = 'M'; if((fp = fopen("info.txt", "w+")) == NULL) printf("can't open the file! \n"); else fprintf(fp, "%d, %s, %c", num, name, gender); //将数据格式化输出到文件info.txt中 fscanf(fp, "%d, %s, %c", &num, name, &gender); //从文件info.txt中格式化读取数据 printf("%d, %s, %c \n", num, name, gender); //格式化输出到屏幕 fclose(fp);
二进制文件操作
数据块存取函数
函数原型:
_CRTIMP size_t __cdecl fread(void *, size_t, size_t, FILE *); _CRTIMP size_t __cdecl fwrite(const void *, size_t, size_t, FILE *);
当要求一次存取一组数据(如,一个数组、一个结构体变量的值),fread和fwrite函数可以解决该类问题。它们的调用形式一般为:
fread(buffer, size, count, fp);
fwrite(buffer, size, count, fp);
buffer:对于fread来说,指的是读入数据的存放地址;对于fwrite来说,是要输出数据的地址(缓冲区)。
size:读写数据时,每笔数据的大小。
count:读写数据的笔数。
fp:文件指针。
/* 读取二进制文件演示 */ FILE *fp; fp = fopen("path","rb+"); if(fp == NULL){ //WARNING }else{ int a[2]; fread(a,4,2,fp); //读2个单元存储于数组中,每个单元4个字节 } fclose(fp);
/* 写入二进制文件演示 */ FILE *fp; fp = fopen("path","wb+"); if(fp == NULL){ //WARNING }else{ int a[2] = {1,2}; fwrite(a,4,2,fp); //写数组中2个单元,每个单元4个字节 } fclose(fp);
随机存取函数fseek()
函数原型:
_CRTIMP int __cdecl fseek(FILE *, long, int);
对流式文件可以进行顺序读写,也可以进行随机读写。关键在于控制文件的位置指针,如果位置指针是按字节位置顺序移动的,就是顺序读写。如果能将位置指针按需要移动到任意位置,就可以实现随机读写。所谓随机读写,是指读完上一个字符(字节)后,并不一定要读写其后续的字符(字节),而可以读写文件中任意位置上所需要的字符(字节)。该函数的调用形式为:
fseek(fp, offset, start);
start:起始点。用0、1、2代替。0代表文件开始,名字为SEEK_SET,1代表当前位置,名字为SEEK_CUR,2代表文件末尾,名字为SEEK_END。
offset:以start为几点,以字节为单位的偏移量;当offset为正整数,表示位置指针从指定的起始点向后移动,当offset为负整数的时候,表示位置指针从指定的起始点向前移动。
fseek()函数一般用于二进制文件,因为文本文件要发生字符转换,计算位置时往往会发生混乱。
调用实例如:
fseek(fp, i*sizeof(Person), 0);
我趣,爱了