一.文件的种类从文件功能的角度来分类的1.1程序文件包括源程序文件后缀为.c, 目标文件windows 环境后缀为.obj, 可执行程序windows 环境后缀为.exe。1.2数据文件文件的内容不一定是程序而是程序运行时读写的数据比如程序运行需要从中读取数据的文件或者输出内容的文件。1.3文件名文件名包括三部分文件路径文件名主干文件后缀例如c:\code\test.txt二.文件的打开和关闭2.1文件指针每个被使用的文件都会在内存中开辟一个相应的文件信息区用来存放文件的相关信息这些信息保存在一个结构体变量中 该结构体类型有系统声明取名为FILE也就是说FILE是一个结构体 。此为VS2013提供的stdio.h头文件中有的文件类型申明当我们使用fopen打开文件时会在内存里创建文件信息区并且会将文件信息区的起始地址返回回来一般都是通过FILE的指针来维护FILE结构中的变量即FILE* pf //文件指针变量可通过pf指向某个文件的文件信息区再通过文件信息区访问文件即通过文件指针变量能够找到与它关联的文件。2.2文件的打开和关闭在打开文件时都会返回一个FILE*的指针变量指向该文件根据规定使用fopen函数来打开文件使用fclose来关闭文件。fopen:格式FILE* fopen(const char* filename, const char* mode); //FILE*是fopen的返回类型第一个参数是文件名相对路径。第二个参数是打开模式常用的有模式含义核心特性r只读打开已存在的文件文件不存在则打开失败w只写文件不存在则创建文件已存在则清空内容覆盖//在fopen打开文件时清空a追加文件不存在则创建写入内容追加到文件末尾r读写打开已存在的文件可读可写不清空内容w读写文件不存在则创建已存在则清空内容可读可写a读写文件不存在则创建写入追加到末尾可读如果在后面加上b,即rb、wb等就表示是二进制模式fclose:格式int fclose ( FILE * stream );使用示例#includestdio.h #includestring.h #includeerrno.h int main() { //打开文件 FILE*pffopen(text.c,r);//若打开失败则pf会返回空指针 if(pfNULL) { printf(%s\n,strerror(errno)); return 1; } //关闭文件 fclose(pf); pfNULL; return 0; }路径分为相对路径和绝对路径相对路径以当前所在目录为起点简写路径依赖当前位置。 例text.c绝对路径从根目录开始完整、唯一的路径不受当前所在位置影响。 例C:\Users\hpl13\Desktop\text.c注当在代码中使用fopen打开绝对路径时应用\\ 例 FILE*pffopen(C:\\Users\\hpl13\\Desktop\\text.c,r);三.文件的顺序读写3.1关于文件读写的函数功能函数名适用于字符输入函数读一个字符fgetc所有输入流字符输出函数写一个字符fputc所有输出流文本行输入函数读一行字符fgets所有输入流文本行输出函数写一行字符fputs所有输入流格式化输入函数fscanf所有输入流格式化输出函数fprintf所有输入流二进制输入fread文件二进制输出fwrite文件输入读输出写键盘输入、文件读取、网络数据接收本质上都是数据从外部流向程序这叫输入流。屏幕打印、文件写入、网络数据发送本质上都是数据从程序流向外部这叫输出流。fgetc(读一个字符)格式int fgetc ( FILE * stream );使用示例#includestdio.h #includestring.h #includeerrno.h int main() { //打开文件 FILE*pffopen(text.c,r); if(pfNULL) { printf(%s\n,strerror(errno)); } //读文件 int chfgetc(pf); printf(%c\nch);//一次只读一个字符 //以下方法可将文件内的内容全部读出来 int ch0; while((chfgetc(pf))!EOF) { printf(%c ,ch); } //关闭文件 fclose(pf); pfNULL; return 0; }fputc(写一个字符)格式int fputc ( int character, FILE * stream );第一个参数是写入的字符。第二个参数是文件指针变量名。使用示例#includestdio.h #includestring.h #includeerrno.h int main() { //打开文件 FILE*pffopen(text.c,w); if(pfNULL) { printf(%s\n,strerror(errno)); } //写文件 int i0; for(ia;iz;i); { fputc(i,pf); } //关闭文件 fclose(pf); pfNULL; return 0; }fgets(读一行字符)格式char * fgets ( char * str, int num, FILE * stream );第一个参数是指向一个字符数组的指针读取的字符串将被复制到该数组中。第二个参数是读取的字节数包括\0,且\0算一个字符。第三个参数是文件指针变量名。使用示例#includestdio.h #includestring.h #includeerrno.h int main() { //打开文件 FILE*pffopen(text.c,r); if(pfNULL) { printf(%s\n,strerror(errno)); } //读一行字符 char arr[20]; fgets(arr,5,pf); printf(%s,arr) //关闭文件 fclose(pf); pfNULL; return 0; }fputs(写一行字符)格式int fputs ( const char * str, FILE * stream );使用示例#includestdio.h #includestring.h #includeerrno.h int main() { //打开文件 FILE*pffopen(text.c,w); if(pfNULL) { printf(%s\n,strerror(errno)); } //写一行字符 fputs(hello world,pf); //关闭文件 fclose(pf); pfNULL; return 0; }补充perror头文件#includestdio.h当我们判断pf是否为空指针NULL时可用perror来代替printf(%s,strerror(errno));例FILE*pffopen(text.c,w); if(pfNULL) { perror(fopen:); }结果fprintf(格式化写入)格式int fprintf ( FILE * stream, const char * format, ... );//与printf相似只比printf多了第一个参数使用示例struct S { char name[10]; int age; float weight; }; int main() { struct S s{zhangsan,13,50.2}; FILE*pffopen(text.c,w); if(pfNULL) { perror(fopen); } fprintf(pf,%s %d %f,s.name,s.age,s.weight); fclose(pf); pfNULL; return 0; }fscanf(格式化读)格式int fscanf ( FILE * stream, const char * format, ... );//与scanf相似只比scanf多了第一个参数使用示例struct S { char name[10]; int age; float weight; }; int main() { struct S s{0}; FILE*pffopen(text.c,r); if(pfNULL) { perror(fopen); } fscanf(pf,%s %d %f,s.name,(s.age),(s.weight)); printf(%s %d %f,s.name,s.age,s.weight); fclose(pf); pfNULL; return 0; }补充流任何一个C语言程序只要运行起来就默认打开三个流FILE* stdin - 标准输入流(键盘)FILE* stdout - 标准输出流(屏幕)FILE* stderror - 标准错误流(屏幕)如果将fprintf的第一个参数由pf换为stdout那么就会写入到屏幕上对于fputs和fputc也一样fwrite(二进制方法写)格式size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );第一个参数是指向你要写入文件的数据的起始地址数据从哪里来第二个参数是一个元素的大小第三个参数是有几个这样的元素使用实例FILE*pf{test.c,wd}; int arr[] {10,20,30}; fwrite(arr, sizeof(int), 3, pf);FILE*pf{test.c,wd}; struct S s{zhangsan,18,65.2f}; fwrite(s,sizeof(struct S),1,pf);通过二进制的方式写进去这会让我们肉眼看不懂这时就应该用fread以二进制的方式来读fread二进制方法读格式size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );//与fwrite相同使用示例FILE*pf{test.c,rd}; struct S s{zhangsan,18,65.2}; fread(s,sizeof(struct S),1,pf); printf(%s %d %f,s.arr,s.age,s.weight);3.2关于一些函数的差异scanf 是针对标准输入的格式化输入语句键盘printf 是针对标准输出的格式化输出语句键盘fscanf 是针对所有输入流的格式化输入语句文件、键盘输入都可以fprintf 是针对所有输出流的格式化输出语句文件、键盘输入都可以sscanf作用从一个字符串中转化出一个格式化的数据#includestdio.h struct S { char name[20]; int age; float weight; }; int main() { struct S s{zhangsan,18,65.2f}; struct S tmp { 0 }; char buf[100] { 0 }; //把s中的格式化数据转化成字符串放到buf中 sprintf(buf, %s %d %f, s.arr, s.age, s.score); //zhangsan 20 55.500000; printf(字符串: %s\n, buf); //从字符串buf中获取一个格式化的数据到tmp中 sscanf(buf, %s %d %f, tmp.arr, (tmp.age), (tmp.score)); printf(格式化: %s %d %f\n, tmp.arr, tmp.age, tmp.score); return 0 }sprintf作用把一个格式化的数据写到字符串中本质是把一个格式化的数据转换成字符串。格式int sprintf ( char * str, const char * format, ... );//与printf相似只比其多了第一个参数使用示例#includestdio.h struct S { char name[20]; int age; float weight; }; int main() { struct S s{zhangsan,18,65.2f}; char buf[100]{0}; sprintf(buf,%s %d %f,s.name,s.age,s.weight); printf(%s\n,buf); return 0; }四.文件的随机读写4.1fseek作用指定文件流的读写位置想从文件哪里读 / 写就把指针移到哪里。格式int fseek ( FILE * stream, long int offset, int origin );第二个参数是偏移量第三个参数是起始位置常量参考位置SEEK_SET文件开头SEEK_CUR文件指针的当前位置SEEK_END文件末尾使用示例#includestdio.h int main() { FILE*pffopen(test.c,r);//test.c文件里存放的内容abcdef if(pfNULL) { perror(open:); return 1; } fseek(pf,2,SEEK_SET);//与文件开头位置的偏移量为2(偏移量可以为负数) int chfputc(pf); printf(%c,ch);//c fseek(pf,2,SEEK_CUR); chfputc(pf); printf(%c,ch);//f return 0; }疑问第二fseek为什么偏移量是 2 而不是 3解释关键在于fgetc读取一个字符后文件指针会自动向后移动 1 位。4.2ftell作用返回文件指针当前的读写位置距离文件开头的字节偏移量。格式long int ftell ( FILE * stream );使用示例fseek(pf,2,SEEK_SET);//与文件开头位置的偏移量为2(偏移量可以为负数) int chfputc(pf); printf(%c\n,ch);//c printf(%d\n,ftell(pf));//3 fseek(pf,2,SEEK_CUR); chfputc(pf); printf(%c,ch);//f printf(%d\n,ftell(pf));//64.3rewind作用让文件指针的位置回到文件的起始位置格式void rewind ( FILE * stream );使用示例fseek(pf,2,SEEK_SET); int chfputc(pf); printf(%c\n,ch);//c printf(%d\n,ftell(pf));//3 rewind(pf); chfputc(pf); printf(%c,ch);//a printf(%d\n,ftell(pf));//1五.文本文件和二进制文件根据数据的组织形式数据文件被称为文本文件或者二进制文件二进制文件数据在内存中以二进制的形式存储不加转换的输出到外存我们看不懂文本文件在外存上以ASCLL字符的形式存储的文件我们看得懂数据在内存中的存储方式字符一律以ASCLL形式存储数值型数据既可以以ASCLL形式存储也可以以二进制形式存储以10000为例把10000转换为16进制也就是10000 0x00002710当为大段存储时00 00 27 10当为小段存储时10 27 00 00六.文件读取结束的判定6.1错误地使用feof6.1.1feof作用检测上一次读操作是否遇到了文件末尾千万不能用来预判文件有没有结束应用于判断是否读取失败结束还是遇到文件尾结束格式int feof ( FILE * stream );返回值文件结束 → 返回非 0 值没结束 → 返回 0feof(pf)!0:上一次读操作正常碰到文件末尾正常结束使用示例char buf[100]; while(fgets(buf, 100, fp) ! NULL) { printf(%s, buf); } if(feof(fp)) { printf(正常读到末尾\n); }6.1.2ferror格式int ferror ( FILE * stream );作用判断是否因【读取错误】结束返回值上一次文件操作发生错误-返回非 0;操作正常无错误-返回 0ferror(fp) ! 0上一次读操作发生 IO 错误异常失败结束总结feof只管是不是撞到文件尾、正常读完ferror只管是不是读写错误、异常失败6.1.3用来判断文件是否读取结束的方法1.文本文件是否读取结束,判断返回值是否为EOF(fgetc)或者NULL(fgets)fgetc判断是否为EOFfgets判断返回值是否为NULL2.二进制文件是否读取结束,判断返回值是否小于实际要读的个数fread判断返回值是否小于实际要读的个数count是我们实际要读的个数(我们要fread读的),而fread的返回值等于fread实际读到值的个数文件读到末尾已经读完文件里所有有效数据此时缓冲区还有最后一个有效数据文件读取结束在读完所有数据后又尝试再读一次读操作失败七.文件缓冲区文件缓冲区是C 语言标准库在内存中开辟的一块临时存储区域用来暂存程序和磁盘文件之间的数据。缓冲区的大小根据编译系统决定数据先写到输出缓冲区中等到输出缓冲区满了后再一次性写到硬盘中但是也有库函数可以设置缓冲区的大小因为有缓冲区的存在C 语言在操作文件的时候需要做刷新缓冲区或者在文件操作结束的时候关闭文件。如果不做可能导致读写文件有问题