文件读写全套易错笔记
一、语法类错误(错误代码+修正代码)
错误1:fopen不做空指针判断
错误代码
FILE*fp=fopen("test.txt","r");fputc('a',fp);// 打开失败fp=NULL,直接崩溃问题:文件打开失败不会捕获,后续读写直接程序崩溃。
修正代码
FILE*fp=fopen("test.txt","r");if(fp==NULL){perror("文件打开失败");return1;}fputc('a',fp);错误2:绝对路径、转义符、路径格式错误
错误代码
// 问题1:单斜杠\是转义字符,识别失败FILE*fp=fopen("C:\Users\test.txt","rb");// 问题2:绝对路径绑定本机,换电脑直接找不到文件FILE*fp=fopen("D:/user/123/b.txt","rb");推荐写法
// 双反斜杠转义绝对路径FILE*fp=fopen("C:\\Users\\test.txt","rb");// 最优:文件放exe同目录,使用相对路径FILE*fp=fopen("test.txt","rb");错误3:打开模式和读写操作不匹配
错误代码
FILE*fp=fopen("data.txt","r");fputs("123",fp);// r只读模式执行写入,运行失败要点:r/rb仅可读;w/wb/a/ab仅可写,模式和操作必须对应。
二、中文乱码问题
- 文本文件乱码
现象:写入中文,记事本打开乱码。
原因:VS默认编码GBK,记事本默认UTF‑8解析,字节编码不匹配。
解决:
- 记事本另存为,编码选择ANSI;
- 统一全部代码、文件编码为UTF‑8;
- 测试优先使用ASCII英文数字。
- 二进制文件损坏乱码
原因:文本模式会自动转换\n与\r\n,破坏二进制字节。
解决:图片、视频、压缩包读写必须加b,使用rb/wb/ab。
三、三大写入函数返回值区分(核心易混点)
1. fputc(单个字符写入)
原型:int fputc(int ch, FILE* fp);
成功:返回字符ASCII十进制值,如'a'返回97。
失败:返回EOF(-1)。
intret=fputc('a',fp);printf("%d",ret);// 输出972. fputs(字符串写入)
原型:int fputs(const char* str, FILE* fp);
成功:固定返回0。
失败:返回EOF(-1)。
注意:返回值为状态码,禁止用%c打印。
intret=fputs("hello",fp);printf("%d",ret);// 成功输出03. fwrite(二进制块批量写入)
原型:size_t fwrite(const void* buf, size_t 块大小, size_t 块数量, FILE* fp);
返回值:实际成功写入的完整块个数,不是字节数。
判断失败:返回值 < 传入块总数。
总字节 = 返回值 × 单个块大小。
返回值是无符号数,禁止用%c打印。
4. fread(二进制读取)
原型:size_t fread(void* buf, size_t 块大小, size_t 块数量, FILE* fp);
返回值:读到的完整块个数;读取完毕返回0;不能用EOF判断结束。
返回值汇总表
| 函数 | 成功返回值 | 失败/结束判断 | 可用%c打印? |
|---|---|---|---|
| fputc | 字符ASCII码(97) | ret == EOF | ✅可以 |
| fputs | 固定0 | ret == EOF | ❌不建议 |
| fwrite | 成功写入的块数量 | 返回值<预期块数 | ❌禁止 |
| fread | 读到的块数量 | 返回值 == 0 | ❌禁止 |
fread‑fwrite拷贝易错点(货车模型通俗解析)
变量类比
arr相当于货车车厢,1024是车厢最大容量;n记录本趟实际装载字节数。fread(缓冲区, 单块大小, 最多读多少块, 源文件)
‑ arr:货车车厢
‑ 1:1块=1字节
‑ 1024:一趟最多拉1024字节
‑ file1:源文件
循环执行流程:
fread读取数据存入缓冲区,实际读到字节数赋值给n;n != 0:有数据就拷贝,n=0代表文件读完,循环结束。
fwrite(arr,1,n,file2)
‑ n只写入本次读到的数据;若固定写1024,缓冲区残留垃圾数据,文件末尾乱码。
完整运行流程:
- 二进制模式打开源文件file1(rb)、目标文件file2(wb);
- 缓冲区单次读取1024字节:读满就写1024字节,末尾不足就只写n字节;
- fread返回0,文件读取完毕,退出循环;
- 关闭文件,拷贝完成。
易错点:
- fwrite第三个参数必须填n,不能写1024;
- 二进制文件必须使用rb/wb;
- 缓冲区大小可改为4096、8192,缓冲区越大拷贝速度越快;
- n规范类型为size_t(无符号类型),int可运行但不规范。
错误缓冲区写法:
// 错误,最后一次写入垃圾数据while(1){fread(arr,1,1024,file1);fwrite(arr,1,1024,file2);}正确模板:
charbuf[1024];size_tn;while((n=fread(buf,1,1024,file1))!=0){fwrite(buf,1,n,file2);}拓展坑点:数组类型匹配
intarr[20];// 错误fread(arr,1,20,fp);// 正确,块大小使用sizeof(int)fread(arr,sizeof(int),20,fp);四、char数组底层原理易错点
代码:char arr[] = {97,98,99};
- 内存底层只存储数字97、98、99;
- char占1字节,fwrite第二个参数填1刚好匹配;
- 记事本按照ASCII码将字节翻译为abc;
- int数组每个元素4字节,块大小必须改为
sizeof(int),否则数据错乱乱码; - 二进制数组没有
'\0'结束符,和字符串规则区分开。
五、完整无BUG代码
1. 二进制文件拷贝代码(图片、视频通用)
#define_CRT_SECURE_NO_WARNINGS#include<stdio.h>#include<stdlib.h>intmain(){// 二进制模式打开源文件、目标文件FILE*file1=fopen("src.txt","rb");FILE*file2=fopen("dest.txt","wb");// 空指针容错判断if(file1==NULL||file2==NULL){perror("打开失败");return1;}chararr[1024];// 货车缓冲区,最大1024字节size_tn;// 保存本次实际读取字节数while((n=fread(arr,1,1024,file1))!=0){fwrite(arr,1,n,file2);}fclose(file1);fclose(file2);system("pause");return0;}2. 文本读写示例(fputc/fputs)
#define_CRT_SECURE_NO_WARNINGS#include<stdio.h>#include<stdlib.h>intmain(){FILE*fp=fopen("test.txt","w");if(fp==NULL){perror("打开失败");return1;}fputc('A',fp);fputs("Hello World",fp);fclose(fp);system("pause");return0;}六、通用注意事项(全部踩坑汇总)
文件打开模式区分
‑w/wb:清空原有文件内容,从头写入;
‑r/rb:只读,文件不存在直接打开失败;
‑a/ab:末尾追加写入,不覆盖旧内容;
‑ 二进制文件读写必须带b(rb/wb/ab),防止换行符篡改文件。必须执行fclose()
‑ 刷新缓冲区,把内存数据写入磁盘,避免数据丢失;
‑ 释放文件锁。EOF使用规则
EOF(-1)仅用于fputc、fputs;fread读取结束判断返回值是否为0。数据类型规范
fread、fwrite返回值类型是size_t(无符号整数),接收变量优先用size_t,禁止用int。VS编译警告配套处理
‑ system()未定义:添加头文件#include <stdlib.h>;
‑ fopen警告:代码最顶部写#define _CRT_SECURE_NO_WARNINGS,放在所有头文件前面;强制规则清单
① fopen之后立刻做空指针判断,避免空指针崩溃;
② Windows路径必须转义\\或使用/;
③ 二进制文件模式必须带b;
④ 文件读写完毕一定要fclose;
⑤ 打开模式和读写操作保持匹配;
⑥ fread结束条件判断返回值是否为0,禁止使用EOF;
⑦ 宏定义写在头文件最前面。
