详解C++编程中对二进制文件的读写操作

林舟
林舟
发布时间:2026-10-13 | 4人浏览过
原创

C++中的二进制文件以内存存储形式直接映射到磁盘,效率高且无需字符转换。本文将演示如何利用ofstream和ifstream的write/read方法进行批量读写,并结合fstream类与指针控制函数seekg/seekp,实现二进制数据的随机访问与原地修改。

二进制文件读写原理与核心函数

二进制文件是将内存中的数据不加转换地传送到磁盘的文件,又称“字节文件”。与ASCII文本文件不同,二进制文件包含非字符形式的字节数据,因此读写时不能依靠空格或换行符判断边界,而必须精确指定读写的字节数。

在C++标准库中,二进制文件的操作主要依赖输入输出流对象的两个核心成员函数:

  • 写操作:ostream& write(const char *buffer, int len)
  • 读操作:istream& read(char *buffer, int len)

其中,buffer指向内存中的一段存储空间地址,len为要读写的数据字节数。由于这两个参数均使用char*类型作为形参,在处理结构体、数组等非字符类型数据时,通常需要将其强制转换为字符指针(如(char*)&obj)。

第1步:向二进制文件写入数据

目的:将一组结构体数据高效保存至磁盘文件。
入口:创建ofstream对象并指定ios::binary标志。
动作:使用write方法逐个写入结构体元素,或直接写入整个数组。
关键参数:文件路径、打开模式ios::binary。
完成状态:成功生成对应大小的二进制文件(如.dat),且程序无报错退出。

以下代码展示了如何定义一个学生结构体,并通过循环将3名学生数据逐条写入stud.dat文件中。需要注意的是,如果一次性写入所有数据,可以直接传入数组首地址和总大小:

#include <iostream>
#include <fstream>
using namespace std;

struct student {
  char name[20];
  int num;
  int age;
  char sex;
};

int main() {
  // 初始化学生数据
  student stud[3] = {"Li", 1001, 18, 'f', "Fun", 1002, 19, 'm', "Wang", 1004, 17, 'f'};
  
  // 以二进制模式打开输出文件
  ofstream outfile("stud.dat", ios::binary);
  if (!outfile) {
    cerr << "open error!" << endl;
    return 1; // 建议使用return代替abort
  }
  
  // 方式A:循环写入每个元素
  for(int i = 0; i < 3; i++) {
    outfile.write((char*)&stud[i], sizeof(stud[i]));
  }
  
  // 方式B:一次性写入全部数据(推荐)
  // outfile.write((char*)&stud[0], sizeof(stud));
  
  outfile.close();
  return 0;
}

该方法的优点在于数据紧凑,写入速度快。在后续读取时,不需要解析格式,而是严格依据字节长度提取数据。

第2步:从二进制文件读取数据

目的:将磁盘中的二进制流还原为内存结构体对象。
入口:创建ifstream对象并指定ios::binary标志。
动作:调用read函数将数据存入缓冲区。
重要参数:目标内存地址(需强制转换)、固定字节数。
完成状态:内存结构体数组被正确填充,可通过控制台验证输出结果。

读取时的结构体定义应与写入时保持一致,否则会导致内存错位或乱码。同样支持逐条读取或整体读取:

#include <iostream>
#include <fstream>
using namespace std;

// 注意:这里结构与写入时一致,若写入时使用静态数组,读取也应匹配
struct student {
  char name[20];
  int num;
  int age;
  char sex;
};

int main() {
  student stud[3];
  ifstream infile("stud.dat", ios::binary);
  
  if (!infile) {
    cerr << "open error!" << endl;
    return 1;
  }
  
  // 逐条读取
  for(int i = 0; i < 3; i++) {
    infile.read((char*)&stud[i], sizeof(stud[i]));
    cout << "Name: " << stud[i].name
         << " Num: " << stud[i].num
         << " Age: " << stud[i].age
         << " Sex: " << stud[i].sex << endl;
  }
  
  infile.close();
  return 0;
}

第3步:随机访问与数据更新

目的:在不重写整个文件的前提下,精准定位并修改某条记录。
入口:使用fstream类同时开启输入输出模式(ios::in | ios::out | ios::binary)。
动作:利用seekg(输入指针)和seekp(输出指针)移动文件内部游标。
关键参数:位移量(单位字节)、参照位置(ios::beg, ios::cur, ios::end)。
完成状态:指定记录的内容已在物理文件中更新,再次读取可看到新值。

对于二进制文件,我们可以根据偏移量计算指针位置。假设每条记录固定大小为S,则第N条记录的起始地址偏移量为(N-1) * S。这允许我们实现数据库式的增删改查操作。

#include <iostream>
#include <fstream>
#include <cstring>
using namespace std;

struct student {
  int num;
  char name[20];
  float score;
};

int main() {
  student stud[5] = {
    1001, "Li", 85.0, 1002, "Fun", 97.5,
    1004, "Wang", 54.0, 1006, "Tan", 76.5,
    1010, "ling", 96.0
  };
  
  // 使用 fstream 允许同时读写
  fstream iofile("stud.dat", ios::in | ios::out | ios::binary);
  if (!iofile) {
    cerr << "open error!" << endl;
    return 1;
  }

  // 1. 首先将所有学生数据写入文件
  for (int i = 0; i < 5; i++) {
    iofile.write((char*)&stud[i], sizeof(stud[i]));
  }

  // 2. 随机读取:读取第1个和第3个学生的数据
  cout << "--- Read Odd Students ---" << endl;
  for(int i = 0; i < 5; i += 2) {
    // seekg用于移动输入文件指针
    iofile.seekg(i * sizeof(student), ios::beg); 
    student temp;
    iofile.read((char*)&temp, sizeof(temp));
    cout << "Num:" << temp.num << " Name:" << temp.name << " Score:" << temp.score << endl;
  }

  // 3. 修改数据:更新第3个学生(索引为2)的信息
  student updateRec = {1012, "Wu", 60.0};
  
  // seekp用于移动输出文件指针
  iofile.seekp(2 * sizeof(student), ios::beg); // 定位到第三个元素的开头
  iofile.write((char*)&updateRec, sizeof(updateRec));

  // 4. 验证修改:从头读取所有数据
  cout << "--- Verify Update ---" << endl;
  iofile.seekg(0, ios::beg); // 重置输入指针
  for(int i = 0; i < 5; i++) {
    iofile.read((char*)&stud[i], sizeof(stud[i]));
    cout << "Num:" << stud[i].num << " Name:" << stud[i].name 
         << " Score:" << stud[i].score << endl;
  }

  iofile.close();
  return 0;
}

在执行上述随机访问时,需注意以下几点:
1. 参照位置常量:ios::beg表示文件头,ios::cur表示当前位置,ios::end表示文件尾。
2. 指针独立性:在有输入输出的情况下,seekg只影响读取位置,seekp只影响写入位置,二者互不干扰。
3. 类型匹配:写入和读取的结构体内存布局必须完全一致,任何字段类型的改变都会导致错位。

总结

C++处理二进制文件的核心在于字节级的精确控制。通过write/read可以高效地序列化/反序列化复杂数据结构;通过seekg/seekp则能实现类似关系型数据库索引的快速查找与局部更新。相比反复打开关闭文件或全量重写,这种基于fstream的模式在处理大型配置表或日志数据时具有显著的性能优势。
以上就是C++中对二进制文件的读写操作的详细内容,更多关于C++文件流处理技巧的资料请关注本站其它相关文章!

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表