
目录文件名文件的分类流的概念文件指针文件的打开和关闭文件的打开文件的关闭文件的顺序读写1.fputc()2.fgetc()3.feof()4.ferror()5.fputs()6.fgets()7.fprintf()8.fscanf()9.sprintf()10.sscanf()11.fwrite()12.fread()文件的随机读写1.fseek()2.fetell()3.rewind()文件缓冲区fflush()更新文件关键点文件名文件名文件路径文件名主干文件后缀这里的文件路径可以是绝对路径也可以是相对路径。绝对路径完整的路径相对路径相对于当前文件夹的路径“.”代表当前文件“..”代表相对于与当前的上一级路径文件的分类按照功能分类程序文件数据文件按照存储方式进行分类二进制文件内存中的数据不加转换就直接以二进制存储在文件中文本文件内存中的数据转换为ASCII存入文件中流的概念介于外部设备和程序之间的一个抽象概念便于程序和外部设备数据的交互。我们想要向流里面写数据或者从流中读取数据都要先打开流在操作。标准流C语言程序启动时会默认打开3个流stdin--标准输入流从键盘输入scanf()就是从标准输入流中读取数据的stdout--标准输出流输出值显示器界面上printf()函数就是将数据输出到标准输出流中stderror--标准错误流大多数环境输出到显示器上。这三个流的类型FILE*, 通常称文件指针。C语言中是通过FILE*的文件指针来维护流的各种操作的。文件指针每一个被使用的文件都在内存中开辟了一个相应的文件信息区用来存放文件的相关信息文件名、文件状态、文件所在位置等这些信息保存在一个结构体变量中该结构体由系统声明取别名为FILE一般通过一个FILE的指针来维护这个FILE结构体变量用起来方便创建文件指针变量FILE* pf使pf指向某个文件的文件信息区一个结构体变量通过该文件信息区中的信息就能访问文件。通过文件指针变量能够间接找到与他关联的文件以及和文件相关联的文件流。文件的打开和关闭文件的打开文件的打开主要用fopen()函数函数声明FILE* fopen(const char* filename,const char* mode );filename:表示被打开的文件名字可以是绝对路径也可以是相对路径mode:表示对要打开的文件的操作模式的选择具体有哪些模式下图返回值打开成功返回一个指向FILE对象的文件指针该指针用于后续操作、标识对应的流。打开失败返回NULL因此在调用此函数后要对返回值进行判断判断文件是否打开成功在进一步对文件进行操作。文件的关闭文件的关闭主要用fclose()函数函数声明为int fclose(FILE* stream);关闭文件指针变量stream关联的文件并取消其关联关系。与该流关联的所有内部缓冲区均会解除关联并刷新任何为写入的输出缓冲区内容将被写入文件任何未读取的输入缓冲区的内容将被抛弃。返回值成功关闭stream指向的文件返回0否则返回EOF.#includestdio.h int main() { //打开文件 FILE* pf fopen(test1.txt, r); if (pf NULL) { perror(fopen); return 1; } //关闭文件 fclose(pf); pf NULL;//避免pf成为野指针 return 0; }文件的顺序读写1.fputc()字符写入函数函数声明int fputc(int character,FILE* stream);将character指定的字符写入到stream所指向的输出流中通常用于文件和标准输出流写入字符在写入字符后会调整文件指示器文件内容的光标。character:被写入的字符只能一个一个的写入stream:文件指针指向输出流文件流或者标准输出流stdout返回值成功返回写入的字符以int形式失败返回EOF通常为-1错误指示器会被设置可用ferror()检查具体错误。#includestdio.h int main() { //打开文件 FILE* pf fopen(Test1.txt, w); if (pf NULL) { perror(fopen); return 1; } //文件操作 for (char ch a;ch z;ch)//将a-z26个字母写入文件中 { fputc(ch, pf); } //关闭文件 fclose(pf); pf NULL; return 0; }2.fgetc()字符读取函数函数定义int fgetc(FILE* stream);从参数stream指向的流中读取一个字符读取后文件指示器将自动前进到下一个字符处。stream可以指向文件流也可以指向标准输入流stdin返回值成功返回读取的字符失败如果调用时流已经处于文件末尾函数返回EOF并且设置流的文件结束指示器如果调用时发生读取错误函数返回EOF并且设置文件错误指示器。#includestdio.h int main() { //打开文件 FILE* pf fopen(Test1.txt, r); if (pf NULL) { perror(fopen); return 1; } //文件操作 char a fgetc(pf); printf(%c, a); //关闭文件 fclose(pf); pf NULL; return 0; }3.feof()文件末尾检测函数函数定义int feof(FILE* stream);在读取过程中遇到文件的末尾文件读取会结束读取函数会在对应的流上设置文件结束指示符而feof()函数可以检测出这个文件结束指示符是否设置了。返回值检测到文件结束指示符已被设置返回非0值检测到文件结束指示符未被设置返回04.ferror()文件错误检测函数函数定义int ferror(FILE* stream);在读取或者写入文件过程中发生了读写错误文件的读写操作会结束。并且读写函数会在对应流上设置错误指示符而ferror()函数可以检测这个错误指示符是否被设置。返回值检测到错误指示符已被设置返回非0值检测到错误指示符未被设置返回0#includestdio.h int main() { //打开文件 FILE* pf fopen(Test1.txt, r); if (pf NULL) { perror(fopen); return 1; } //文件操作 char a fgetc(pf); if (fgetc(pf) EOF)//一般先判断读取错误再判断文件结束 { if (ferror(pf)) { printf(发生读取错误); } else if (feof(pf)) { printf(遇到文件末尾); } } else { printf(%c, a); } //关闭文件 fclose(pf); pf NULL; return 0; }5.fputs()字符串写入函数函数定义int fputs(const char* str,FILE* stream);将str指向的字符串写入到stream指向的流中不包含结尾的\0str:指向要写入到文件的字符串stream:指向想要操作的输出流可以是文件流也可以是标准输出流返回值成功返回一个非负整数失败返回EOF,同时设置错误指示符可用ferror()检查。#includestdio.h int main() { //打开文件 FILE* pf fopen(Test1.txt, w); if (pf NULL) { perror(fopen); return 1; } //操作文件 char str[] abcde; fputs(str, pf); //关闭文件 fclose(pf); pf NULL; return 0; }6.fgets()字符串读取函数函数定义char* fputs(char* str,int num,FILE* stream);从stream指向的流中读取num个字符存储到str指向的内存空间中若num大于文件中有的字符个数读取到文件末尾就会结束。遇到换行符会提前结束str:指向用于存储读取到字符串的内存空间num:最大读取字符数num中包含末尾的\0,所以实际读取字符个数num-1个stream:指向想要读取的输入流可以是文件流也可以是标准输入流。返回值成功返回str指针失败若遇到读取错误在对应流上设置错误指示符返回NULL可用ferror()检测若再读取过程中遇到文件末尾再对应流上设置文件结束指示符返回NULL可用feof()检测。#includestdio.h int main() { //打开文件 FILE* pf fopen(Test1.txt, r); if (pf NULL) { perror(fopen); return 1; } //操作文件 char str[30] {0}; char* p fgets(str,5, pf); if (p NULL) { if (ferror(pf)) { printf(发生读取错误); } else if (feof(pf)) { printf(遇到文件末尾); } } else { printf(%s , str); } //关闭文件 fclose(pf); pf NULL; return 0; }PS: 将调用函数的表达式放在判断条件处也会再一次进行函数的执行7.fprintf()格式化数据输出函数函数定义int fprintf(FILE* stream,const char* format,...);将格式化数据写入到输出流中包含文件流和标准输出流与“printf”相似但是可以写入的输出流更广泛。参数比printf()函数多了一个文件指针指向要写入的输出流。返回值成功返回写入的字符总数非负值失败在对应流上设置错误指示符再返回负值可用ferror()检测。#includestdio.h struct std { char name[10]; int age; int class; }; int main() { //打开文件 FILE* pf fopen(Test1.txt, w); if (pf NULL) { perror(fopen); return 1; } //操作文件 struct std s1 { xiao ming,18,22 }; fprintf(pf, 姓名%s 年龄%d 班级%d , s1.name, s1.age, s1.class); //关闭文件 fclose(pf); pf NULL; return 0; }8.fscanf()格式化数据输入函数函数定义int fscanf(FILE* stream,const char* format,...);从指定输入流中读取格式化数据与“scanf”相似但是输入流更加广泛可以是标准输入流也可以是文件流适用于从文件中解析出结构化数据。stream:文件指针指向要读取的输入流返回值成功读取返回填充到参数列表中的项数该值可能与预期一致也可能少于预期原因格式和数据匹配失败发生读取错误读取时遇到文件结尾失败读取若果在未成功读取任何数据前发生发生读取错误设置对应流的错误指示符并返回EOF;遇到文件末尾设置对应流的文件结束指示符并返回EOF.#includestdio.h struct std { char name[10]; int age; int class; }; int main() { //打开文件 FILE* pf fopen(Test1.txt, r); if (pf NULL) { perror(fopen); return 1; } //操作文件 struct std s2 {0}; int a fscanf(pf, 姓名%s 年龄%d 班级%d , s2.name, s2.age, s2.class); //如果要从文件中读取数据文件中的数据格式和fscanf中数据格式必须保持一致否则会读错数据 if ( a EOF) { if (ferror(pf)) { printf(发生读取错误); } else if (feof(pf)) { printf(遇到文件末尾); } } else { printf(姓名%s 年龄%d 班级%d , s2.name, s2.age, s2.class); } //关闭文件 fclose(pf); pf NULL; return 0; }9.sprintf()将格式化数据转换为一个字符串函数定义int sprintf(char* str,const char* format,...);str:指向字符串数组的指针用于存储合成后的字符串。返回值成功返回写入buffer中的字符数不包含结尾的\0;失败返回负值。常用于动态生成字符串#includestdio.h struct std { char name[10]; int age; int class; }; int main() { struct std s1 { xiao ming,18,22 }; char str[50] { 0 };//用于存储生成的字符串的内存空间一定要足够大 sprintf(str, 姓名%s 年龄%d 班级%d, s1.name, s1.age, s1.class); fputs(str, stdout);//两种方式都可以将字符串输出到显示器屏幕上 //printf(%s ,str); return 0; }10.sscanf()从字符串中读取格式化数据与scanf()相似但是输入源来自内存中的字符串函数定义int sscanf(char* str,const char* format,...);str:指向要解析的字符串的指针。返回值成功返回成功解析并赋值的参数数量非负值失败或者未匹配到任何数据返回EOF.#includestdio.h struct std { char name[10]; int age; int class; }; int main() { struct std s1 { 0 }; char str[50] 姓名xiaoming 年龄18 班级22; int a sscanf(str, 姓名%s 年龄%d 班级%d, s1.name, s1.age, s1.class); //从str中解析出格式化数据 printf(%d \n, a); //解析的参数个数 fprintf(stdout, 姓名%s 年龄%d 班级%d, s1.name, s1.age, s1.class); //将格式化数据写入到标准输出流中屏幕显示器上 return 0; }11.fwrite()二进制数据块写入函数函数定义size_t fwrite(const void* ptr,size_t size,size_t num,FILE* stream);以二进制形式将数据块写入stream指向的文件流当中。ptr:指向被写入的数据块的指针size:每一个数据项的内存大小以字节为单位num:要写入的数据项数量stream:指向要写入的输出流。一般为文件流返回值成功返回成功写入的数据项数量发生错误时返回值可能小于count.注意事项必须包含头文件stdio.h;使用fwrite()之前确保文件已经以二进制可写方式打开了若要用fwrite()写文本数据要谨慎处理换行符和编码问题。#includestdio.h int main() { //打开文件 FILE* pf fopen(Test1.txt, wb);//文件操作方式的设置 if (pf NULL) { perror(fopen); return 1; } //操作文件 int arr[7] { 1,2,3,4,5,6,7 }; fwrite(arr, 4, 5, pf);//将数组中数据转换成二进制写入到文件流中 //关闭文件 fclose(pf); pf NULL; return 0; }12.fread()二进制数据读取函数函数定义size_t fread(void* ptr,size_t size,size_t num,FILE* stream);将stream指向的输入流中的二进制数据读取出来存储到ptr指向的数据块内存空间中。ptr:指向存储读取出来的二进制数据的内存空间的指针size:每一个数据项的内存大小以字节为单位num:要读取的数据项数量stream:文件指针指向要读取的输入流一般是文件流。返回值返回实际读取的数据项个数。注意事项必须包含头文件stdio.h;使用fread()之前确保文件已经以二进制可读方式打开了ptr所指向的空间必须足够大以便于存储读取出来的数据如果fread()成功读取了指定数量的数据项则返回值等于count如果读取数量少于coumt则可以在读取过程中已经到达文件末尾后者发生了读取错误在二进制文件读取时用fread()但是对于文本文件的读取通常使用fgets(),fscanf().#includestdio.h int main() { //打开文件 FILE* pf fopen(Test1.txt, rb);//文件操作方式的设置 if (pf NULL) { perror(fopen); return 1; } //操作文件 int arr[10] { 0 }; fread(arr, 4, 5, pf);//从文件中读取五个大小为四个字节的数据项 int i 0; for (i 0;i 5;i) { printf(%d , arr[i]);//输出读出的数据 } //关闭文件 fclose(pf); pf NULL; return 0; }文件的随机读写1.fseek()文件内容光标设置函数函数声明int fseek(FILE* stream,long int offset,int origin);根据文件指针的位置和偏移量来定位文件内容的光标。stream:指向要定位文件内容光标的文件流offset:相对于基准的偏移量基准由“origin”决定若为正数向文件末尾移动若为负数向文件开头移动若为0则不移动origin:偏移的起始位置可以取三个值SEEK_SET文件起始位置SEEK_CUR文件指针当前位置SEEK_END文件末尾位置。返回值设置成功返回0设置失败返回非零值-1注意事项对于二进制文件fseek()的行为非常直观可以精确的移动到任何字节的位置对应于文本文件fseek()的行为在某些环境下不确定原因不同系统对于换行符的转换不同Linux:“\n”;Windows:“\r\n”#includestdio.h int main() { //打开文件 FILE* pf fopen(Test1.txt, r); if (pf NULL) { perror(fopen); return 1; } //操作文件 fseek(pf, 2, SEEK_SET);//从文件起始位置光标向后移动两位 char a fgetc(pf); printf(%c \n, a); fseek(pf, -1, SEEK_END);//从文件末尾位置光标向前移动一位 char b fgetc(pf); printf(%c , b); //关闭文件 fclose(pf); pf NULL; return 0; }2.fetell()文件内容光标位置计算函数函数声明long int ftell(FILE* stream);计算文件光标位置相对于文件起始位置的偏移量。返回值成功返回当前文件光标相对于文件起始位置的偏移量字节数失败返回-1注意事项对于二进制文件ftell()的返回值就是相对于文件开头的精确偏移量对于文本文件由于不同操作系统对于换行符的处理不同如Windows\r\nLinux\nftell()返回值可能不精确但是大多数情况它仍然可以安全的于fseek()配合使用来记录和恢复位置。#includestdio.h int main() { //打开文件 FILE* pf fopen(Test1.txt, r); if (pf NULL) { perror(fopen); return 1; } //操作文件 fseek(pf, 2, SEEK_SET); long int pos ftell(pf);//记录当前偏移量 char a fgetc(pf); printf(%c \n, a); fseek(pf, -1, SEEK_END); char b fgetc(pf); printf(%c \n, b); fseek(pf, pos, SEEK_SET);//由记录的偏移量恢复文件内容光标恢复至记录时状态 char c fgetc(pf); printf(%c \n, c); //关闭文件 fclose(pf); pf NULL; return 0; }3.rewind()将文件内容光标回复至文件起始位置函数定义void rewind(FILE* stream);注意事项rewind相当于完全初始化不仅初始化文件内容光标文件指针还会清除文件的错误标志和文件结束标志于fseek()对比fseek(pf,0,SEEK_SET):只移动文件指针不清除错误指示符rewind(pf):移动文件指针并清除错误指示符和文件结束指示符。#includestdio.h int main() { //打开文件 FILE* pf fopen(Test1.txt, r); if (pf NULL) { perror(fopen); return 1; } //操作文件 fseek(pf, 0, SEEK_SET); char a fgetc(pf); printf(%c \n, a); rewind(pf);//对文件内容光标进行复位 char b fgetc(pf); printf(%c \n, b);//两次输出的字符一样均为a //关闭文件 fclose(pf); pf NULL; return 0; }文件缓冲区系统自动在内存中为程序中每一个正在使用的文件开辟一块“文件缓冲区”从内存向硬盘中写入数据时会先将数据存放在二者之间的文件输出缓冲区当该缓冲区装满或者被强制刷新时数据回从缓冲区写入硬盘中从硬盘向内存输入数据时会先将数据存放在二者之间的文件输入缓冲区当该缓冲区装满或者被强制刷新时会进行一些操作行为由具体实现决定极大提升了操作系统的各种效率。fflush()函数定义int fflush(FILE* stream);强制刷新stream指向流的缓冲区确保数据写入底层设备。对输出流将缓冲区未写入的数据立即写入输出流中对输入流行为由具体实现决定非C语言标准行为可能清空输入缓冲区当参数为NULL时刷新所有打开的输出流。返回值成功返回0失败返回EOF.注意事项仅对输出流和更新流由明确的刷新行为输入流的刷新行为不可移植如非C语言标准行为可能清空输入缓冲区程序正常终止exit),或者调用fclose()时会主动刷新单程序崩溃时缓冲区数据可能会丢失。更新文件主要包括三种模式“r”,“w”“a”,具体内容如下图关键点在进行了文件读写操作后文件指示器回留在操作后的位置不会主动复位。在写完文件后要继续读文件时在读取前一定要使用fflush()强制刷新缓冲区再使用fseek()、rewind()对定义文件指示器位置在读完文件要继续写文件时再写文件前使用fseek()、rewind()对定义文件指示器位置。