1. 为什么使用文件
2. 什么是文件
3. 二进制文件和文本文件
4. 文件的打开和关闭
5. 文件的顺序读写
6. 文件的随机读写
7. 文件缓冲区
8.更新文件
1. 为什么使用文件
文件的核心作用就是:把数据从易失的内存,存到持久的硬盘里,程序退出了数据也不会丢。
2. 什么是文件
2.1程序文件
程序文件就是和 “程序本身”相关的文件,比如.c源代码、编译生成的.obj目标文件、最终的.exe可执行文件,这些都是用来构成和运行程序的。
2.2数据文件
数据文件就是程序运行时用来读写数据的文件,内容不一定是代码,比如程序运行需要从中读取数据的文件,或者输出内容的文件,程序从里面读数据,也能把结果写到里面去。
2.3文件名
文件名其实是文件的唯一标识,用来定位和区分不同文件,完整的标识包含三部分:
文件路径:文件在磁盘上的位置,比如 c:\code\
文件名主干:文件的名字,比如 test
文件后缀:表示文件类型,比如 .txt
举个例子:c:\code\test.txt
路径:c:\code\
主干:test
后缀:.txt
3. 二进制文件和文本文件
| 存储方式 | 存储规则 | 示例(整数10000) | 占用空间 |
| 文本文件(ASCLL形式) | 所有数据都按 ASCII 字符存储,数值会被拆成字符 | '1' '0' '0' '0' '0' | 5 字节 |
| 二进制文件 | 直接把内存中的二进制数据原封不动写入文件 | 00000000 00000000 00100111 00010000 | 4 字节(int 类型占 4 字节) |
关键区别:
文本文件:人能直接看懂(比如用记事本打开),但会额外占用空间,读写时需要转换。
二进制文件:和内存中的数据结构完全一致,读写速度快,空间更省,但人无法直接看懂。
3.1整数10000的两种存储方式
1.内存中的形式10000 作为 int 类型,在内存中是 4 字节的二进制:00000000 00000000 00100111 00010000(十进制转二进制结果)。
2.ASCII 形式(文本文件)会把 10000 拆成 5 个字符:'1' '0' '0' '0' '0',每个字符的 ASCII 码是:
'1' → 00110001(49)
'0' → 00110000(48)所以文件里会存这 5 个字节:00110001 00110000 00110000 00110000 00110000,共 5 字节。
3.二进制形式(二进制文件)直接把内存中的 4 字节原封不动写入文件,文件里就是:00000000 00000000 00100111 00010000,共 4 字节。用 VS 的二进制编辑器打开,会看到十六进制:00 00 27 10(和二进制一一对应)。
3.2演示代码解析
#include <stdio.h> int main() { int a = 10000; FILE* pf = fopen("test.txt", "wb"); // "wb" 表示:以二进制模式写文件 fwrite(&a, 4, 1, pf); // 从变量a的地址开始,写4个字节,写1次 fclose(pf); pf = NULL; return 0; }fopen的 "wb" 模式是关键:w 表示写,b 表示二进制(binary),不加 b 就是文本模式。
fwrite直接把 a 的内存数据原封不动写入文件,所以生成的是二进制文件。
我们发现文件会产生乱码的情况,这是因为文本文档中不是在二进制文件中,并且直接打开是会产生弹窗。
这时我们就要改变它的打开方式,将其改为二进制的打开方式
这时生成结果就是下面的二进制序列
3.3VS 中查看二进制文件的方法
1.在 VS 的「解决方案资源管理器」中,右键 test.txt。
2.选择「打开方式」。
3.在弹出的窗口中,选择「二进制编辑器」。
4.打开后就能看到文件的十六进制内容:00 00 27 10,和我们前面分析的一致。
4. 文件的打开和关闭
4.1流和标准流
4.1.1流
1流是 C 语言中对外部设备(文件、键盘、屏幕等)数据交互的抽象概念,可以理解为数据流动的通道。
2.所有输入输出操作(如键盘输入、屏幕打印、文件读写),都是通过 “流” 来完成的。
3.流操作自带缓冲区,目的是减少直接读写硬盘的次数,提高效率。
4.1.2标准流
C 程序启动时,会自动打开 3 个流,无需手动 fopen:
| 流名 | 含义 | 用途 |
| stdin | 标准输入流 | 从键盘读取数据,scanf 函数使用它 |
| stdout | 标准输出流 | 向屏幕打印数据,printf 函数使用它 |
| stderr | 标准错误流 | 向屏幕输出错误信息 |
这三个流的类型都是 FILE*(文件指针),和文件操作的指针是同一种类型。
4.2文件指针
每个被打开的文件,都会在内存中创建一个文件信息区(FILE 结构体变量),存储文件名、状态、读写位置等信息。
FILE* 就是指向这个结构体的指针,我们通过它来间接操作文件。
不同编译器的 FILE 结构体细节不同(如 VS 的 struct _iobuf),但用法一致,开发者无需关心内部实现。
4.3文件的打开和关闭
4.3.1fopen
//打开⽂件 FILE * fopen ( const char * filename, const char * mode );1.参数:
filename:要打开的文件名(可以是相对路径或绝对路径)。
绝对路径"盘符:/文件夹/文件名.后缀" "E:/project/data.txt"相对路径
同级:直接写文件名 子级:文件夹/文件名 上级:../文件名mode:文件打开方式(核心参数,决定读写权限和文件类型)。
2.返回值:
成功:返回一个 FILE* 类型的指针,指向该文件的信息区。
失败:返回 NULL,必须做判空处理,否则会引发程序崩溃。
#include<stdio.h> int main() { FILE* pf= fopen("test.txt","r"); if (pf == NULL) { perror("fopen"); return 1; } //读文件 //关闭为文件 fclose(pf); pf = NULL; }常用mode 对照表
| 模式 | 含义 | 文本不存在时 | 文本存在时 |
“r” | 只读(文本文件) | 报错 | 打开,只读 |
| “w” | 只写(文本文件) | 创建新文件 | 清空原有内容,只写 |
| “a” | 追加(文本文件) | 创建新文件 | 在文件末尾追加数据 |
| “rb” | 只读(文本文件) | 报错 | 打开,只读 |
| “wb” | 只写(文本文件) | 创建新文件 | 清空原有内容,只写 |
| “ab” | 追加(文本文件) | 创建新文件 | 在文件末尾追加数据 |
| “r+” | 读写(文本文件) | 报错 | 打开,可读写 |
| “w+” | 读写(文本文件) | 创建新文件 | 清空原有内容,可读写 |
| “a+” | 读写(文本文件) | 创建新文件 | 末尾追加 + 读 |
4.3.2fclose
int fclose(FILE *stream);功能:关闭文件,刷新缓冲区,释放文件信息区的资源。
返回值:成功返回 0,失败返回 EOF。
!!!注意:文件使用完必须关闭,否则会导致数据未写入、资源泄漏。关闭后建议将指针置为 NULL,避免野指针。
#include <stdio.h> int main() { // 1. 打开文件,只读模式 FILE* fp = fopen("test.txt", "r"); if (fp == NULL) // 必须判空! { perror("fopen"); // 打印错误信息 return 1; } printf("文件打开成功,可以进行读写操作\n"); // 2. 关闭文件 fclose(fp); fp = NULL; // 置空,避免野指针 return 0; }4.4避坑指南与关键细节
1.必须判空:fopen 失败会返回 NULL,如果直接使用空指针操作文件,会导致程序崩溃。
2.关闭文件:不关闭文件会导致缓冲区数据未写入硬盘,文件内容丢失;同时占用系统资源。
3.文本和二进制模式:
文本模式下,Windows 系统会自动将 \n 转换为 \r\n 写入文件,读取时再转换回来。
二进制模式下,数据原封不动读写,不会做任何转换,适合图片、视频等非文本数据。
4.路径问题:Windows 下路径分隔符是 \,但 C 语言中 \ 是转义字符,所以路径要写成 D:\\test.txt 或 D:/test.txt。
5."w" 模式会清空文件:使用 "w" 或 "wb" 打开已存在的文件时,文件内容会被直接清空,操作前一定要确认。
5.文件的顺序读写
5.1函数总览
| 函数名 | 功能 | 适用范围 |
| fgetc | 从输入流读取单个字符 | 所有输入流(文件、stdin) |
| fputc | 从输出流读取单个字符 | 所有输出流(文件、stdin) |
| fgets | 从输入流读取字符串 | 所有输入流 |
| fputs | 从输出流读取字符串 | 所有输出流 |
| fscanf | 从输入流读取格式化数据 | 所有输入流 |
| fprintf | 从输出流读取格式化数据 | 所有输出流 |
| fread | 读取一块二进制数据 | 仅文件输入流 |
| fwrite | 写入一块二进制数据 | 仅文件输出流 |
5.2字符读写函数
5.2.1fputc
int fputc ( int character, FILE * stream );1.参数:
character:待写入字符
stream:文件指针 / 输出流(stdout--屏幕)
2.返回值:成功返回写入的字符(int 型);失败返回EOF(-1)
代码演示:写入单个字符
#include <stdio.h> int main() { FILE* fp = fopen("test.txt", "w"); // w模式:清空原有内容,新建文件写入 if(fp == NULL) { perror("fopen"); return 1; } fputc('a', fp); fputc('b', fp); fputc('c', fp); fclose(fp); // 关闭文件 fp = NULL; // 置空避免野指针 return 0; }此时test.txt文件中将是abc
代码演示2:循环写入 a-z 所有字符
#include <stdio.h> int main() { FILE* fp = fopen("test.txt", "w"); if(fp == NULL) { perror("fopen"); return 1; } int ch = 0; for(ch = 'a'; ch <= 'z'; ch++) { fputc(ch, fp); } fclose(fp); fp = NULL; return 0; }此时test.txt文件中将是a到z所有字符
5.2.2fgetc
int fgetc ( FILE * stream );参数:stream:文件指针 / 输入流(stdin--键盘)
返回值:成功返回读取的字符(int 型);读到末尾 / 出错返回EOF(-1)
代码演示1:读取文件前 10 个字符并打印前 10 个字符
#include <stdio.h> int main() { FILE* fp = fopen("test.txt", "r"); // r模式:只读 if(fp == NULL) { perror("fopen"); return 1; } int i = 0; for(i = 0; i < 10; i++) { int c = fgetc(fp); fputc(c, stdout); // stdout:标准输出(控制台) } fclose(fp); fp = NULL; return 0; }代码演示2:读取文件中所有数据并打印文件中所有数据
#include<stdio.h> int main() { FILE* pf = fopen("test.tst", "r");//读文件 if (pf == NULL) { perror("fopen"); return 1; } int ch = 0; while ((ch = fgetc(pf)) != EOF) { printf("%c",ch); } fclose(pf);//关闭文件 pf = NULL; return 0; }5.2.3feof和ferror
feof
int feof ( FILE * stream ); //检测stream指针指向的流是否遇到⽂件末尾参数:stream:文件指针
返回值:到文件末尾返回非 0;未到末尾返回 0
ferror
int ferror ( FILE * stream ); //检测stream指针指向的流是否发⽣读/写错误参数:stream:文件指针
返回值:读写出错返回非 0;无错误返回 0
代码演示1:feof 判断文件末尾
#include <stdio.h> int main() { FILE* fp = fopen("test.txt", "r"); if(fp == NULL) { perror("fopen"); return 1; } int c = 0; while((c = fgetc(fp)) != EOF) // 循环读取,直到末尾 { fputc(c, stdout); } if(feof(fp)) printf("读到文件末尾\n"); else if(ferror(fp)) printf("读取出错\n"); fclose(fp); fp = NULL; return 0; }代码演示2:ferror 判断读写错误
#include <stdio.h> int main() { FILE* fp = fopen("test.txt", "w"); // 只写模式,不能读,会触发错误 if(fp == NULL) { perror("fopen"); return 1; } int c = fgetc(fp); // 尝试读取,触发错误 if(c == EOF) { if(ferror(fp)) printf("读取出错\n"); } fclose(fp); fp = NULL; return 0; }5.3字符串读写函数
5.3.1fputs
int fputs ( const char * str, FILE * stream );参数:
str:待写入字符串
stream:文件指针 / 输出流
返回值:成功返回非负数;失败返回EOF(-1)
代码演示1:写入字符串
//fputs输入字符串到文件中 #include<stdio.h> int main() { FILE* pf = fopen("test.tst", "w");//写文件 if (pf == NULL) { perror("fopen"); return 1; } fputs("hello", pf); fputs("world", pf); fclose(pf);//关闭文件 pf = NULL; return 0; }此时test.txt文件中将是helloworld
5.3.2fgets
char * fgets ( char * str, int num, FILE * stream );参数:
str:存储读取内容的字符数组
num:最大读取字符数(包含结尾的\0,实际最多放num-1个字符)
stream:文件指针 / 输入流
返回值:成功返回str 地址;末尾 / 出错返回NULL
代码演示1:读取文件两行内容
#include <stdio.h> int main() { FILE* fp = fopen("test.txt", "r"); if(fp == NULL) { perror("fopen"); return 1; } char arr1[10] = {0}; char arr2[10] = {0}; fgets(arr1, sizeof(arr1), fp); // 读取第一行 fgets(arr2, sizeof(arr2), fp); // 读取第二行 printf("%s", arr1); printf("%s", arr2); fclose(fp); fp = NULL; return 0; }或者运用循环
//用fgets读取文件 #include<stdio.h> int main() { FILE* pf = fopen("test.tst", "r");//写文件 if (pf == NULL) { perror("fopen"); return 1; } char arr[20] = "-----------"; /*fgets(arr, 5, pf);*///abcd\0 //如果有多行数据 运用循环 while ((fgets(arr, 20, pf)) != NULL) { printf("%s ", arr); } fclose(pf);//关闭文件 pf = NULL; return 0; }5.4格式化读写函数
5.4.1fprintf
int fprintf ( FILE * stream, const char * format, ... ); //类⽐printf函数学习参数:
stream:文件指针 / 输出流
format:格式字符串
...:可变参数(待写入数据)返回值:成功返回写入字符个数;失败返回负数
代码演示1:写入结构体数据
//用fprintf写入文件 #include<stdio.h> struct Stu { char name[20]; int age; float score; }; int main() { struct Stu s = { "zhangsan", 20, 95.5f }; FILE* pf = fopen("test.tst", "w");//写文件 if (pf == NULL) { perror("fopen"); return 1; } fprintf(pf,"名字:%s 年龄;%d 成绩:%f\n",s.name ,s.age,s.score ); fclose(pf);//关闭文件 pf = NULL; return 0; }此时test.txt文件中将是zhangsan", 20, 95.5f
5.4.2fscanf
int fscanf ( FILE * stream, const char * format, ... ); //类⽐scanf函数来学习参数:
stream:文件指针 / 输入流
format:格式控制字符串(同scanf)
...:变量地址列表
返回值:成功函数返回成功填充到参数列表中的项数。如果在成功读取任何数据之前发生:1.发生读取错误,会在对应流上设置错误指示符,则返回EOF。2.到达文件末尾,会在对应流上设置⽂件结束指⽰符,则返回EOF。
代码演示1:读取文件中结构体数据
//用fscanf读取数据 #include<stdio.h> struct Stu { char name[20]; int age; float score; }; int main() { struct Stu s = { "zhangsan", 20, 95.5f }; FILE* pf = fopen("test.tst", "w");//写文件 if (pf == NULL) { perror("fopen"); return 1; } fscanf(pf, "名字:%s 年龄:%d 成绩:%f", s.name, &(s.age), &(s.score)); fprintf(stdout, "名字:%s 年龄:%d 成绩:%.2f\n", s.name, s.age, s.score); fclose(pf);//关闭文件 pf = NULL; return 0; }5.5sprintf 和 sscanf
5.5.1sprintf
int sprintf ( char * str, const char * format, ... );功能
把格式化数据写入字符数组(字符串),相当于把 printf 的输出从屏幕改成内存字符串。
参数
str:字符数组指针,存储生成的字符串(空间要足够,防止溢出)
format:格式字符串(%d、%f、%s 等)
...:可变参数,对应格式的数据
返回值
成功:返回写入的字符数(不含末尾\0)
失败:返回负值
示例(核心就是把内容转换成字符串 并存放)
struct Stu s = {"zhangsan", 100, 85.5f}; char arr[100] = {0}; sprintf(arr, "%s %d %.1f", s.name, s.age, s.score);这个代码就是将结构体s中的内容转换成一个字符串
5.5.2sscanf
int sscanf ( const char * str, const char * format, ...);功能
从字符串里读取格式化数据,相当于把 scanf 的输入从键盘改成内存字符串。
参数
str:源字符串(数据来源)
format:格式字符串
...:变量地址列表
返回值
成功:返回成功读取赋值的参数个数
失败 / 到末尾:返回 EOF(-1)
示例(核心是从字符串中解析出一个格式化的数据)
char str[] = "zhangsan 100 85.5"; struct Stu s = {0}; sscanf(str, "%s %d %f", s.name, &s.age, &s.score);把sprintf和sscanf结合起来使用
//ssprintf //把格式化数据 转换成字符串并存放在内存中 #include<stdio.h> struct Stu { char name[20]; int age; float score; }; int main() { struct Stu s = { "zhangsan", 20, 95.5f }; char arr1[30] = { 0 }; sprintf(arr1, "%s %d %.2f", s.name, s.age, s.score);//存放进字符串内存区 printf("%s\n", arr1); //sscanf //从arr1中解析处一个结构体数据 struct Stu t = { 0 }; sscanf(arr1, "%s %d %f", t.name, &(t.age), &(t.score)); sprintf(stdout, "%s %d %.2f", s.name, s.age, s.score);//存放进字符串内存区 return 0; }此时输出结果是下面这张图
5.5.3 6组格式化函数总对比
| 函数 | 作用 | 操作对象 |
| scanf | 格式化输入 | 标准输入 stdin |
| printf | 格式化输出 | 标准输出 stdout |
| fscanf | 格式化输入 | 文件流 / stdin |
| fprintf | 格式化输出 | 文件流 / stdout |
| sprintf | 格式化输出 | 内存字符串 |
sscanf | 格式化输入 | 内存字符串 |
5.6二进制批量读写函数
5.6.1fwrite(从文件中读取数据到格式化变量中)
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );参数
ptr:待写入数据的内存首地址
size:单个数据的字节大小
count:要写入的数据个数
stream:文件指针
示例:写入数组二进制数据
//fwrite的使用 #include<stdio.h> struct Stu { char name[20]; int age; float score; }; int main() { struct Stu s = { "zhangsan", 20, 95.5f }; FILE* pf = fopen("test.tst", "wb");//写二进制文件 if (pf == NULL) { perror("fopen"); return 1; } //写一个结构体类型的内容 到文件中去 fwrite(&s, sizeof(struct Stu), 1, pf); fclose(pf); pf = NULL; return 0; }5.6.2fread 二进制读取(从文件中读取数据到格式化变量中)
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );功能:批量写二进制
参数:const void* buf, size_t size, size_t count, FILE* stream
返回:实际写入的块数
示例:读取二进制结构体数据
//fread读取二进制数据 #include<stdio.h> struct Stu { char name[20]; int age; float score; }; int main() { struct Stu s = { 0 }; FILE* pf = fopen("test.tst", "rb");//读取二进制文件 if (pf == NULL) { perror("fopen"); return 1; } //读取文件中的数据 fread(&s, sizeof(struct Stu), 1, pf); printf("%s %d %.2f\n", s.name, s.age, s.score); fclose(pf); pf = NULL; return 0; }此时输出结果是
5.7核心编码规范
1.打开文件必须判断NULL,用perror("fopen")打印错误
2.文本模式:r/w/a;二进制模式:rb/wb/ab
3.用完必须fclose(fp); fp=NULL;
4.读取后用feof、ferror判断状态
6. 文件的随机读写
6.1fseek(移动文件指针)
int fseek ( FILE * stream, long int offset, int origin );功能:定位文件指针,实现随机读写
参数
stream:文件指针
offset:偏移量(正数向后、负数向前、0 不动)
origin:基准位置
1.SEEK_SET:文件开头
2.SEEK_CUR:当前指针位置
3.SEEK_END:文件末尾
返回值:成功返回0;失败返回非 0(-1)
!!!注意:二进制文件可随意偏移;文本文件仅支持SEEK_SET或ftell返回值偏移
//用fseek定位文件指针实现随机打印 //文件中是 abcdefghi #include<stdio.h> int main() { FILE* fp = fopen("test.txt", "r"); if (fp == NULL) { perror("fopen"); return 1; } int c = fgetc(fp);//a fputc(c, stdout);//使用fputc在标准输入流上打印字符 //定位文件指针 fseek(fp, 5, SEEK_SET);//从首个开始定位 向右5个偏移量 fseek(fp, 4, SEEK_CUR);//这是从a开始定位 向右4个偏移量 fseek(fp, -4, SEEK_END);//这是从末尾开始定位 向左4个偏移量 c = fgetc(fp); fputc(c, stdout);//f fclose(fp); fp = NULL; return 0; }此时test.txt文件中将是abcdefghi
标准输出的结果是af
6.2ftell
long int ftell ( FILE * stream );功能:返回文件指针相对于文件开头的字节偏移量
参数:stream:文件指针
返回值:成功返回字节数;失败返回-1L
用途:常和fseek配合,计算文件大小fseek(fp,0,SEEK_END); long size=ftell(fp); // 得到文件总字节大小
6.2.1fseek和ftell的共同使用
//fseek和 ftell的共同使用 #include<stdio.h> int main() { FILE* fp = fopen("test.txt", "r"); if (fp == NULL) { perror("fopen"); return 1; } int c = fgetc(fp); fputc(c, stdout);//输出a int pos = ftell(fp);//这里记录的是b相对于起始位置的偏移量1 //定位文件指针 fseek(fp, 4, SEEK_CUR); c = fgetc(fp); fputc(c, stdout);//这里的是f(相对b往后偏移4) fseek(fp, pos, SEEK_SET);//这里回到初始 偏移量为1 c = fgetc(fp); fputc(c, stdout);//这里就是b fclose(fp); fp = NULL; return 0; }输出结果如下
其中pos记录了第一次的字节数(移动偏移量),用于最后打印b时从f返回到起点
!!!每次移动后都想打印,就要重新用fgetc和fputc
6.3rewind
void rewind ( FILE * stream );功能:文件指针重置到文件开头
关键区别
rewind(fp):移动指针 + 清除错误标志、文件末尾标志fseek(fp,0,SEEK_SET):只移动指针,不清除标志
接着上面的代码用rewind回到起始位置 fputc(c, stdout);//这里就是b rewind(fp);//回到起始位置 c = fgetc(fp); fputc(c, stdout);//这里就是a fclose(fp); fp = NULL; return 0;输出结果如下
7. 文件缓冲区
7.1文件缓冲区概念
ANSI C 规定:系统自动在内存给每个打开的文件开辟一块缓冲区
输出:程序数据 → 输出缓冲区 → 缓冲区满 / 刷新 → 写入硬盘
输入:硬盘数据 → 输入缓冲区 → 程序读取
作用:减少磁盘 IO,提升读写效率
7.2 fflush 函数(强制刷新缓冲区)
int fflush ( FILE * stream );功能
输出流:强制把缓冲区数据立刻写入硬盘
输入流:行为不标准,一般不用
stream=NULL:刷新所有打开的输出流
参数
stream:文件指针 / stdout
返回值
成功输出0 失败输出EOF
刷新缓冲区的 4 种方式
缓冲区写满,自动刷新
调用 fflush(fp),手动强制刷新
调用 fclose(fp) 关闭文件,自动刷新
程序正常结束,自动刷新
核心结论
文件操作必须刷新缓冲区或关闭文件,否则数据可能只在内存,没写到硬盘。
示例代码
FILE* pf = fopen("test.txt", "w"); fputs("abcdef", pf); fflush(pf); // 强制写入磁盘 fclose(pf);8.更新文件
| 行为 | "r+" | "w+" | "a+" |
| 解释 | 可读/可写 | 可读/可写 | 可读/可写 |
| 文件不存在时 | 打开失败 | 自动创建新文件 | 自动创建新文件 |
| 文件存在时 | 保留内容 | 清空内容 | 保留内容 |
| 初始文件指针位置 | 文件开头 | 文件开头 | 文件末尾 |
| 写入是否覆盖所有数据 | 是(可定位覆盖) | 是(内容已清空,从头写入) | 否(默认是在文件末尾写数据) |
| 典型用途 | 修改文件部分内容 | 创建新文件或完全重写旧文件 | 在文件末尾追加数据,比如记录日志 |
关键要点:1. 在写完文件后,要继续读文件的时候,在读取之前⼀定要使用 fflush() 刷新文件缓冲区,或者使用 fseek() , rewind() 重新定位文件指示器的位置。2. 在读完文件后,需要继续写文件之前,在写文件之前,使⽤ fseek() , rewind() 重新定位文件指示器的位置。
#include <stdio.h> int main() { FILE* fp = fopen("test.txt", "w+"); if (fp == NULL) { perror("fopen for w+"); return 1; } //写abcdefghi到⽂件中 fputs("abcdefghi", fp); //刷新缓冲区,保证数据写⼊⽂件 fflush(fp); //要读取数据b字符,先定位⽂件指针 fseek(fp, 1, SEEK_SET); int ch = fgetc(fp);//读取字符 printf("%c\n", ch); //在b的位置开始写⼊hello fseek(fp, -1, SEEK_CUR); //解释:因为前⾯读取⼀个字符后,⽂件指⽰器现在指向了c,需要从当前位置退回⼀个字符fputs("hello", fp); //关闭⽂件 fclose(fp); fp = NULL; return 0; }