知识点#
12.1 文件简介及文本、二进制文件区别#
- @ 文件基础图
- ![[白板/P34 文件基础.canvas|P34 文件基础]]
12.1.1 文件简介#
[!tip] 文件
- @ 文件: 保存在硬盘、U盘等存储介质上的数据
- 操作系统以文件为单位对数据进行管理
- 文件可以看成字符序列
12.1.2 文本文件和二进制文件区别#
[!tip] 文本文件 vs 二进制文件
- @ 文本文件(ASCII 文件): 每个字节存放一个 ASCII 码,代表一个字符,人类能看懂
- @ 二进制文件: 把内存中的数据按存储形式原样输出到磁盘,人类可能看不懂
| 对比项 | 文本文件 | 二进制文件 |
|---|---|---|
| 存储方式 | 每个 ASCII 码占 1 字节 | 按内存存储形式原样保存 |
| 可读性 | 人类能看懂 | 人类可能看不懂(乱码) |
| 存储空间 | 较大(如 10000 占 5 字节) | 较小(如 10000 占 2 字节) |
| 转换开销 | 需要二进制↔ASCII 转换 | 无需转换 |
- ! 对于计算机来讲,==没有文本文件和二进制文件之分==,都是二进制数据
- ! 文本文件可以理解为"人类能看懂的二进制文件"
大端存储与小端存储#
[!tip] 字节序
- @ 小端存储: 低字节存低地址,高字节存高地址
- @ 大端存储: 低字节存高地址,高字节存低地址
- 与 CPU 体系结构有关
// 10000 = 0x2710
// 小端存储: 10 27(低地址→高地址)
// 大端存储: 27 10(低地址→高地址)
12.2 文件的打开、关闭、读写与实战操练#
- @ 文件操作图
- ![[白板/P35 文件操作.canvas|P35 文件操作]]
12.2.1 文件的打开#
[!tip]
fopen函数
- 一般形式:
FILE *fp = fopen(文件名, 文件使用方式);FILE是一个结构体,fp是指向FILE的指针变量(文件指针)- ! 打开失败时返回
NULL
FILE *fp;
fp = fopen("A1", "r"); // 以只读方式打开 A1 文件
if(fp == NULL) {
// 文件打开失败的处理代码
}
常用文件使用方式#
| 文件使用方式 | 含义 |
|---|---|
"r" | 只读,打开文本文件,文件必须存在 |
"w" | 只写,打开文本文件,存在则覆盖,不存在则创建 |
"a" | 追加,向文本文件末尾增加数据 |
"rb" | 只读,打开二进制文件 |
"wb" | 只写,打开二进制文件 |
"ab" | 追加,向二进制文件末尾增加数据 |
"r+" | 读写,打开文本文件 |
"w+" | 读写,建立新的文本文件 |
规律记忆#
- % 有
w→ 往文件中写入 - % 有
r→ 从文件中读出 - % 有
b→ 二进制文件 - % 有
a→ 追加内容到文件末尾 - % 有
+→ 既能读又能写
文件位置指针#
- 每个打开的文件都有一个当前位置指针
- 代表当前从文件的哪个位置开始读/写数据
- 每读出 1 字节数据,位置指针自动后移 1 字节
12.2.2 文件的关闭#
[!tip]
fclose函数
- 一般形式:
fclose(文件指针);- ! 必须关闭文件,原因:
- 释放文件占用的内存资源
- 将缓冲区中的数据写入磁盘,避免数据丢失
if(fp != NULL)
fclose(fp);
- ! 关闭后
fp不能再使用 fclose()返回0表示成功,非0表示失败
12.2.3 文件的读写#
fputc — 写一个字符#
[!tip] fputc
- 调用形式:
fputc(ch, fp);- 将字符
ch写到fp所指向的文件中- 成功返回字符的 ASCII 码,失败返回
EOF
FILE *fp;
fp = fopen("FTest.txt", "w");
if(fp == NULL) {
printf("文件打开失败");
} else {
char reco = fputc('a', fp);
if(reco == EOF) {
// 写失败的处理代码
}
fputc('d', fp);
fputc('e', fp);
fclose(fp);
}
fgetc — 读一个字符#
[!tip] fgetc
- 调用形式:
char reco = fgetc(fp);- 从
fp所指向的文件中读入一个字符- 成功返回读入的字符,失败或到文件末尾返回
EOF
FILE *fp;
fp = fopen("FTest.txt", "r");
if(fp == NULL) {
printf("文件打开失败");
} else {
char reco = fgetc(fp);
while(reco != EOF) {
putchar(reco); // 在屏幕上输出
reco = fgetc(fp); // 继续读
}
fclose(fp);
}
fgets — 读一行字符串#
[!tip] fgets
- 调用形式:
fgets(str, n, fp);- 从
fp指向的文件中读入n-1个字符到str中- 遇到
\n或EOF提前结束- ! 自动在末尾加
\0
fputs — 写一行字符串#
[!tip] fputs
- 调用形式:
fputs(str, fp);- 将字符串
str写到fp指向的文件中
12.2.4 文件读写实战操练#
// 读取配置文件 config.txt
FILE *fp;
fp = fopen("config.txt", "r");
if(fp == NULL) {
printf("文件打开失败\n");
} else {
char LineBuf[1024];
while(fgets(LineBuf, sizeof(LineBuf), fp) != NULL) {
// 处理每一行数据
printf("%s", LineBuf);
}
fclose(fp);
}
12.3 将结构体数据写入二进制文件和从二进制文件读出#
- @ 结构体文件操作图
- ![[白板/P36 结构体文件操作.canvas|P36 结构体文件操作]]
12.3.1 将结构体数据写入二进制文件#
[!tip]
fwrite函数
- 一般形式:
fwrite(buffer, size, count, fp);buffer: 要写入数据的地址size: 要写入的字节数count: 要写入多少个size字节的数据项fp: 文件指针- 返回值: 失败返回
0,成功返回count
#pragma pack(1) // 1字节对齐,避免跨平台问题
struct stu {
char name[30];
int age;
double score;
};
#pragma pack() // 恢复默认对齐
struct stu student[2] = {
{"张三", 18, 80.5},
{"李四", 20, 90.5}
};
FILE *fp;
fp = fopen("structfile.bin", "wb"); // 二进制写方式
if(fp != NULL) {
fwrite(student, sizeof(struct stu), 2, fp);
fclose(fp);
}
结构体写入的坑点#
- ! 结构体成员中不要出现指针类型,指针地址在程序再次运行时会失效
- ! 结构体字节对齐问题:不同编译器/平台对齐方式不同
- Windows VS 可能按 8 字节对齐
- Linux GCC 可能按 4 字节对齐
- 解决方案:
#pragma pack(1)设置 1 字节对齐
12.3.2 从二进制文件中读出结构体数据#
[!tip]
fread函数
- 一般形式:
fread(buffer, size, count, fp);buffer: 读出数据存放的地址size: 要读出的字节数count: 要读出多少个size字节的数据项fp: 文件指针- 返回值: 失败返回
0,成功返回count
FILE *fp;
fp = fopen("structfile.bin", "rb"); // 二进制读方式
if(fp != NULL) {
struct stu studentnew[2];
int retresult = fread(studentnew, sizeof(struct stu), 2, fp);
fclose(fp);
}
12.3.3 文件使用方式中 "r" 与 "rb" 和 "w" 与 "wb" 的区别#
[!warning]
b标记的重要性
- ! 写和读是配套操作,写用了
"b"标记,读也必须用"b"标记- ! 不带
"b"标记读文件时,\r可能被丢弃- ! 不带
"b"标记写文件时,\n可能被自动替换为\r\n(Windows 平台)- ! 带
"b"标记会原封不动地读/写,不做任何转换
| 操作 | 不带 b | 带 b |
|---|---|---|
| 读 | 可能缺失 \r | 原封不动读出 |
| 写 | \n 可能变成 \r\n | 原封不动写入 |
- ! 结论: 希望原封不动地读写文件内容,请在
fopen中使用"b"标记

