C 语言二进制文件
外观
概述[编辑 | 编辑源代码]
二进制文件是以二进制形式存储数据的文件,与文本文件相比具有以下特点:
- 直接存储内存中的原始数据(无字符编码转换)
- 适合保存结构化数据(如结构体、数组等)
- 通常具有更小的文件体积和更快的I/O速度
在C语言中,二进制文件操作主要通过标准库函数实现,使用fopen()
的"rb"、"wb"、"ab"等模式进行访问。
核心概念[编辑 | 编辑源代码]
二进制与文本文件的区别[编辑 | 编辑源代码]
对比项 | 二进制文件 | 文本文件 |
---|---|---|
数据存储 | 原始字节 | 字符编码 |
换行符 | 无特殊处理 | 自动转换(如\n→\r\n) |
文件结尾 | EOF标记 | 特殊字符(如Ctrl+Z) |
适用场景 | 结构化数据 | 人类可读文本 |
文件指针定位[编辑 | 编辑源代码]
二进制文件支持随机访问,关键函数:
fseek(FILE *stream, long offset, int whence)
ftell(FILE *stream)
rewind(FILE *stream)
基本操作[编辑 | 编辑源代码]
写入二进制数据[编辑 | 编辑源代码]
使用fwrite()
函数:
#include <stdio.h>
struct Student {
int id;
char name[20];
float score;
};
int main() {
FILE *fp = fopen("students.dat", "wb");
if (!fp) {
perror("文件打开失败");
return 1;
}
struct Student s = {1001, "张三", 89.5};
size_t written = fwrite(&s, sizeof(struct Student), 1, fp);
printf("成功写入%zu条记录\n", written);
fclose(fp);
return 0;
}
输出示例:
成功写入1条记录
读取二进制数据[编辑 | 编辑源代码]
使用fread()
函数:
#include <stdio.h>
struct Student { /* 同上 */ };
int main() {
FILE *fp = fopen("students.dat", "rb");
if (!fp) {
perror("文件打开失败");
return 1;
}
struct Student s;
size_t read = fread(&s, sizeof(struct Student), 1, fp);
printf("读取记录:ID=%d, 姓名=%s, 分数=%.1f\n",
s.id, s.name, s.score);
fclose(fp);
return 0;
}
输出示例:
读取记录:ID=1001, 姓名=张三, 分数=89.5
高级技巧[编辑 | 编辑源代码]
数据块读写[编辑 | 编辑源代码]
处理数组等连续数据:
int data[5] = {10, 20, 30, 40, 50};
// 写入整个数组
fwrite(data, sizeof(int), 5, fp);
// 读取到新数组
int new_data[5];
fread(new_data, sizeof(int), 5, fp);
结构体对齐问题[编辑 | 编辑源代码]
二进制文件可能遇到内存对齐问题,解决方案:
- 使用
#pragma pack
- 手动填充字节
- 序列化单个字段
实际应用案例[编辑 | 编辑源代码]
案例1:图像处理[编辑 | 编辑源代码]
读写BMP文件头(简化版):
#pragma pack(1) // 禁用对齐
typedef struct {
char signature[2];
int file_size;
short reserved[2];
int data_offset;
} BMPHeader;
void read_bmp(const char* filename) {
FILE *fp = fopen(filename, "rb");
BMPHeader header;
fread(&header, sizeof(BMPHeader), 1, fp);
// 处理头部信息...
}
案例2:游戏存档系统[编辑 | 编辑源代码]
存储玩家进度:
typedef struct {
int level;
float position[3];
char inventory[50];
} GameSave;
void save_game(GameSave *save) {
FILE *fp = fopen("savegame.bin", "wb");
fwrite(save, sizeof(GameSave), 1, fp);
fclose(fp);
}
常见问题[编辑 | 编辑源代码]
字节序问题[编辑 | 编辑源代码]
不同系统可能使用不同字节序(大端/小端),解决方案:
- 使用固定字节序格式(如网络字节序)
- 添加文件魔数标识
- 显式转换数据
错误处理[编辑 | 编辑源代码]
重要检查点:
fopen()
返回值fread/fwrite
的实际读写数量- 文件指针位置验证
性能优化[编辑 | 编辑源代码]
- 使用缓冲区:设置
setvbuf()
- 批量读写:减少I/O操作次数
- 内存映射文件:适用于大文件(POSIX系统)
数学原理[编辑 | 编辑源代码]
文件位置计算: 解析失败 (未知函数“\begin{cases}”): {\displaystyle position = \begin{cases} offset & \text{当 whence = SEEK\_SET} \\ current + offset & \text{当 whence = SEEK\_CUR} \\ filesize + offset & \text{当 whence = SEEK\_END} \end{cases} }
最佳实践[编辑 | 编辑源代码]
1. 始终检查I/O操作返回值
2. 使用sizeof
运算符而非硬编码大小
3. 重要数据添加校验和
4. 考虑平台兼容性
5. 文档化二进制格式