跳转到内容

C 语言二进制文件

来自代码酷
Admin留言 | 贡献2025年4月29日 (二) 04:45的版本 (Page creation by admin bot)

(差异) ←上一版本 | 已核准修订 (差异) | 最后版本 (差异) | 下一版本→ (差异)


概述[编辑 | 编辑源代码]

二进制文件是以二进制形式存储数据的文件,与文本文件相比具有以下特点:

  • 直接存储内存中的原始数据(无字符编码转换)
  • 适合保存结构化数据(如结构体、数组等)
  • 通常具有更小的文件体积和更快的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)

graph LR A[文件开始] -->|SEEK_SET| B(定位点) C[当前位置] -->|SEEK_CUR| B D[文件末尾] -->|SEEK_END| B

基本操作[编辑 | 编辑源代码]

写入二进制数据[编辑 | 编辑源代码]

使用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. 文档化二进制格式