详解C++编程中对二进制文件的读写操作
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++文件流处理技巧的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













