C语言学习笔记20260712-文件操作核心函数详解与实例
C语言学习笔记20260712-文件操作核心函数详解与实例在C语言中程序运行时产生的数据默认存储在内存中一旦程序退出内存被系统回收数据就会永久丢失。文件操作的核心价值在于将数据从内存写入硬盘实现数据的“持久化存储”。C语言标准库stdio.h提供了一套强大的文件操作接口下面将为你详细梳理核心函数、读写方式及易错点。一、 文件的打开与关闭文件操作的第一步是建立程序与硬盘文件的连接使用完毕后必须断开连接。1. fopen打开文件原型FILE *fopen(const char *filename, const char *mode);功能打开指定路径的文件。FILE是一个结构体指针包含了文件缓冲区、当前读写位置等信息是操作文件的“身份证”。返回值成功返回指向该文件的指针失败返回NULL。常用打开模式r只读打开文本文件文件必须存在。w只写打开文本文件若存在则清空内容不存在则新建。a追加打开文本文件写入的数据会附加在文件末尾。rb/wb以二进制模式只读/只写打开适用于非字符数据如图片、音频、结构体。r/w/a以读写模式打开。2. fclose关闭文件原型int fclose(FILE *stream);功能关闭文件并刷新缓冲区确保数据真正写入硬盘。返回值成功返回 0失败返回 EOF。#includestdio.h#includestdlib.hintmain(){// 以只写模式打开或新建一个文本文件FILE*fpfopen(test.txt,w);// 必须检查文件是否打开成功if(fpNULL){perror(fopen failed);return1;}printf(文件打开成功准备写入数据...\n);// 文件操作...// 使用完毕后必须关闭文件if(fclose(fp)!0){perror(fclose failed);return1;}fpNULL;// 防止悬空指针return0;}二、 文件的顺序读写顺序读写是指从文件开头按顺序一笔一笔地读取或写入数据这是最常用的文件操作方式。1. 字符读写fgetc / fputc适用于逐个字符处理文本如统计字符数、过滤特定字符。fgetc从流中读取一个字符成功返回字符的ASCII码失败或读到文件尾返回EOF。fputc向流中写入一个字符成功返回该字符失败返回EOF。// 示例将用户输入的字符写入文件直到输入 $ 为止voidchar_io_example(){FILE*fpfopen(char_test.txt,w);if(fpNULL)return;charch;printf(请输入字符输入$结束);while((chgetchar())!$){fputc(ch,fp);// 写入字符到文件}fclose(fp);}2. 文本行读写fgets / fputs适用于按行处理文本如读取配置文件、日志文件。fgets从流中读取一行或指定大小的字符串到缓冲区。遇到换行符\n或读取满n-1个字符时停止并自动添加\0。成功返回缓冲区地址失败或读到文件尾返回NULL。fputs向流中写入一个字符串不会自动添加换行符。// 示例向文件写入一行文本并重新读取出来voidstring_io_example(){FILE*fpfopen(line_test.txt,w);// 读写模式if(fpNULL)return;constchar*strHello, C Language File!\n;fputs(str,fp);// 写入字符串rewind(fp);// 将文件指针重置到开头准备读取charbuffer{0};if(fgets(buffer,sizeof(buffer),fp)!NULL){printf(读取到的内容%s,buffer);}fclose(fp);}3. 格式化读写fprintf / fscanf适用于处理包含多种数据类型如整数、浮点数、字符串的结构化文本数据。fprintf将格式化数据写入文件类似printf。fscanf从文件中按格式读取数据类似scanf。// 示例保存和读取学生信息structStudent{charname;intage;floatscore;};voidformat_io_example(){structStudentstu{Alice,20,95.5};FILE*fpfopen(student.txt,w);if(fpNULL)return;// 格式化写入fprintf(fp,%s %d %.1f,stu.name,stu.age,stu.score);rewind(fp);// 重置指针structStudentnew_stu;// 格式化读取fscanf(fp,%s %d %f,new_stu.name,new_stu.age,new_stu.score);printf(姓名%s, 年龄%d, 分数%.1f\n,new_stu.name,new_stu.age,new_stu.score);fclose(fp);}4. 二进制读写fread / fwrite适用于高效存储结构体、数组等非字符数据。二进制文件将内存中的数据原封不动地保存存取速度快占用空间小。fwrite向文件写入二进制数据。fread从文件读取二进制数据。// 示例将结构体数组以二进制形式存入文件voidbinary_io_example(){structStudentclass_arr{{Bob,21,88.0},{Cathy,19,92.5}};// 使用 wb 二进制写模式FILE*fpfopen(class.bin,wb);if(fpNULL)return;// 写入缓冲区首地址, 单个元素大小, 元素个数, 文件指针size_twrittenfwrite(class_arr,sizeof(structStudent),2,fp);printf(成功写入 %zu 条记录\n,written);fclose(fp);// 读取演示fpfopen(class.bin,rb);structStudentread_arr;// 读取返回实际成功读取的元素个数size_tread_countfread(read_arr,sizeof(structStudent),2,fp);for(size_ti0;iread_count;i){printf(二进制读取 - 姓名%s\n,read_arr[i].name);}fclose(fp);}三、 文件的随机读写有时我们需要跳过文件中间的部分直接读取或修改特定位置的数据这时需要使用定位函数。fseek重定位文件指针。fseek(fp, offset, whence)。whence可以是SEEK_SET文件开头、SEEK_CUR当前位置、SEEK_END文件末尾。ftell返回当前文件指针相对于文件开头的偏移量字节数。rewind将文件指针重置到文件开头等价于fseek(fp, 0, SEEK_SET)。// 示例修改文件中特定位置的数据voidrandom_access_example(){FILE*fpfopen(data.txt,w);if(fpNULL)return;fputs(Hello World,fp);// 将指针移动到第6个字节处即 W 的位置fseek(fp,6,SEEK_SET);fputs(C,fp);// 将 W 替换为 Crewind(fp);// 回到开头charbuf;fgets(buf,sizeof(buf),fp);printf(修改后的内容%s\n,buf);// 输出: Hello Corldfclose(fp);}四、 核心易错点与避坑指南1. 文件读取结束的正确判定致命误区绝不能直接用feof()的返回值作为循环条件来判断文件是否结束feof只有在“读取失败后”才能准确判断是否是因为遇到了文件尾。文本文件循环条件应判断fgetc() ! EOF或fgets() ! NULL。循环结束后再用feof()或ferror()判断是正常读完还是出错。二进制文件循环条件应判断fread的返回值是否小于实际要读的个数。// 文本文件读取的正确姿势intc;while((cfgetc(fp))!EOF){putchar(c);}// 循环结束后判断原因if(ferror(fp)){puts(读取过程中发生IO错误);}elseif(feof(fp)){puts(文件正常读取完毕);}2. 文件缓冲区机制C语言文件操作默认带有缓冲区全缓冲。调用fprintf或fwrite时数据可能只是暂时存放在内存的缓冲区中并未立刻写入硬盘。刷新缓冲区调用fclose()关闭文件时会自动刷新或者手动调用fflush(fp)强制将缓冲区数据写入硬盘。风险如果程序异常崩溃如被kill -9强杀而未执行fclose缓冲区内的数据将会丢失。3. 标准I/O与系统I/O的区别标准I/O (fopen/fread等)跨平台Windows/Linux通用自带缓冲区效率高基于FILE*指针。系统I/O (open/read/write等)仅限Linux/Unix无缓冲区直接系统调用基于文件描述符整数int fd常用于设备文件驱动开发。日常应用开发推荐优先使用标准I/O。