C语言详解 - 文件 1. 需要了解的概念需要理解的知识点包括数据流、缓冲区、文件类型、文件存取方式1.1 数据流指程序与数据的交互是以流的形式进行的。进行C语言文件的存取时都会先进行“打开文件”操作这个操作就是在打开数据流而“关闭文件”操作就是关闭数据流。1.2 缓冲区(Buffer)指在程序执行时所提供的额外内存可用来暂时存放做准备执行的数据。它的设置是为了提高存取效率因为内存的存取速度比磁盘驱动器快得多。C语言中带缓冲区的文件处理C语言的文件处理功能依据系统是否设置“缓冲区”分为两种一种是设置缓冲区另一种是不设置缓冲区。由于不设置缓冲区的文件处理方式必须使用较低级的I/O函数(包含在头文件io.h和fcntl.h中)来直接对磁盘存取这种方式的存取速度慢并且由于不是C的标准函数跨平台操作时容易出问题。下面只介绍第一种处理方式即设置缓冲区的文件处理方式当使用标准I/O函数(包含在头文件stdio.h中)时系统会自动设置缓冲区并通过数据流来读写文件。当进行文件读取时不会直接对磁盘进行读取而是先打开数据流将磁盘上的文件信息拷贝到缓冲区内然后程序再从缓冲区中读取所需数据如下图所示事实上当写入文件时并不会马上写入磁盘中而是先写入缓冲区只有在缓冲区已满或“关闭文件”时才会将数据写入磁盘如下图所示。1.3 文件类型分为文本文件和二进制文件两种。文本文件是以字符编码的方式进行保存的。二进制文件将内存中数据原封不至文件中适用于非字符为主的数据。如果以记事本打开只会看到一堆乱码。其实除了文本文件外所有的数据都可以算是二进制文件。二进制文件的优点在于存取速度快占用空间小以及可随机存取数据。1.4 文件存取方式包括顺序存取方式和随机存取方式两种。顺序读取也就是从上往下一笔一笔读取文件的内容。保存数据时将数据附加在文件的末尾。这种存取方式常用于文本文件而被存取的文件则称为顺序文件。随机存取方式多半以二进制文件为主。它会以一个完整的单位来进行数据的读取和写入通常以结构为单位。2. 文本文件操作C语言中主要通过标准I/O函数来对文本文件进行处理。相关的操作包括打开、读写、关闭与设置缓冲区。相关的存取函数有fopen(), fclose(), fgetc(), fputc(), fgets(), fputs(), fprintf(), fscanf()等。2.1 打开文件函数原型为_CRTIMP FILE * __cdecl fopen(const char *, const char *);第一参数为文件名第二个参数为打开模式。打开成功fopen返回一个结构指针地址否则返回一个NULL。如果没有指定文件路径则默认为当前工作目录。如FILE *fp;fp fopen(c:\\temp\\test.txt, r) //由于反斜杠\是控制字符所以必须再加一个反斜杠使用fopen()函数打开的文件会先将文件复制到缓冲区。注意所下达的读取或写入动作都是针对缓冲区进行存取而不是磁盘只有当使用fclose()函数关闭文件时缓冲区中的数据才会写入磁盘。谈文件打开模式打开文本文件r只能从文件中读数据该文件必须先存在否则打开失败w只能向文件写数据若指定的文件不存在则创建它如果存在则先删除它再重建一个新文件a向文件增加新数据(不删除原有数据)若文件不存在则打开失败打开时位置指针移到文件末尾r可读/写数据该文件必须先存在否则打开失败w可读/写数据用该模式打开新建一个文件先向该文件写数据然后可读取该文件中的数据a可读/写数据原来的文件不被删去位置指针移到文件末尾打开二进制文件的模式与打开文本文件的含义是一样的不同的是模式名称里面多一个字母b’以表示以二进制形式打开文件。2.2 关闭文件函数原型为_CRTIMP int __cdecl fclose(FILE *);关闭成功返回值0否则返回非零值。注在执行完文件的操作后要进行“关闭文件”操作。虽然程序在结束前会自动关闭所有的打开文件但文件打开过多会导致系统运行缓慢这时就要自行手动关闭不再使用的文件来提高系统整体的执行效率。例1. 打开文件并进行判断和关闭文件FILE *fp;fp fopen(c:\\temp\\test.txt, r);if(fp NULL)printf(fail to open the file! \n);elseprintf(The file is open! \n);fclose(fp);2.3 字符存取函数函数原型为_CRTIMP int __cdecl fputc(int, FILE *);_CRTIMP int __cdecl fgetc(FILE *);字符读取函数fgetc()可从文件数据流中一次读取一个字符然后读取光标移动到下一个字符并逐步将文件的内容读出。如果字符读取成功则返回所读取的字符否则返回EOF(end of file)。EOF是表示数据结尾的常量真值为-1。另外要判断文件是否读取完毕可利用feof()进行检查。未完返回0已完返回非零值。feof()函数原型为_CRTIMP int __cdecl feof(FILE *);例2. fgetc()函数的使用版本1利用feof()函数检查文件是否读取完毕#includemain()FILE *fp;fp fopen(c:\\temp\\test.txt, r);if(fp ! NULL)while(!feof(fp))printf(%c, fgetc(fp));elseprintf(fail to open! \n);fclose(fp);return 0;版本2利用文件结束标志EOF(即-1)#includemain()char ch;FILE *fp;fp fopen(c:\\temp\\test.txt, r);if(fp ! NULL)ch fgetc(fp);while(ch ! EOF)putchar(ch);ch fgetc(fp);elseprintf(fail to open! \n);fclose(fp);return 0;版本3 - 重构版本2#includemain()char ch;FILE *fp;if((fp fopen(test.txt, r)) ! NULL)while((ch fgetc(fp)) ! EOF)putchar(ch);elseprintf(fail to open! \n);fclose(fp);return 0;版本4 - 重构版本3 不正确的重构#includemain()FILE *fp;if((fp fopen(test.txt, r)) ! NULL)while(fgetc(fp) ! EOF)putchar(fgetc(fp));elseprintf(fail to open! \n);fclose(fp);return 0;若要将字符逐一写入文件用fputc()函数。示例为例3. fputc()函数的使用#include#includemain()char filename20, 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);elsewhile((ch getche()) ! \015)fputc(ch, fp);fclose(fp);return 0;2.4 字符串存取函数函数原型为_CRTIMP int __cdecl fputs(const char *, FILE *);_CRTIMP char * __cdecl fgets(char *, int, FILE *);fgets函数的作用是从指定文件读入一个字符串如fgets(str, n, fp);参数n为要求得到的字符个数但只从fp指向的文件输入n-1个字符然后在最后加一个\0字符因此得到的字符串共有n个字符把它们放在字符数组str中。如果在读完n-1个字符之前遇到换行符或EOF读入结束。fputs函数的作用是向指定文件输出一个字符串如fputs(Hey, fp);把字符串Hey输出到fp指向的文件。fputs函数的第一个参数可以是字符串常量、字符数组名或字符型指针。若输出成功则返回0否则返回EOF。实例略2.5 格式化存取函数函数原型为_CRTIMP int __cdecl fprintf(FILE *, const char *, ...);_CRTIMP int __cdecl fscanf(FILE *, const char *, ...);它们与printf和scanf函数相仿都是格式化读写函数。不同的是fprintf和fscanf函数的读写对象不是终端(标准输入输出)而是磁盘文件。printf函数是将内容输出到终端(屏幕)因此fprintf就是将内容输出到磁盘文件了。实例4. fprintf和fscanf函数的使用#includevoid main()FILE *fp;int num 10;char name10 Leeming;char gender M;if((fp fopen(info.txt, w)) NULL)printf(cant open the file! \n);elsefprintf(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);2.6 指针重返函数函数原型为_CRTIMP void __cdecl rewind(FILE *);;rewind函数的作用是使位置指针重返回文件的开头属于文件的定位。3. 二进制文件操作3.1 数据块存取函数函数原型_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文件指针实例5. fread和fwrite函数的使用#include#define SIZE 3typedef enum { MM, GG } Gender;typedef structchar name10int age;