错误地读取或写入二进制文件

Reading or writing binary file incorrectly

本文关键字:二进制文件 读取 错误      更新时间:2023-10-16

代码的输出显示 Student 结构的所有变量的乱码值。当显示功能运行时。

我已经在二进制文件的每个添加和显示函数中包含相关代码。

对于第二个函数,每次运行 for 循环时,seekg指针是否会自动移动以读取下一条记录?

//Student struct
struct Student
{
char name [30];
float labTest;
float assignments;
float exam;
};
//Writing function   
afile.open(fileName,ios::out|ios::binary);
Student S;
strcpy(S.name,"test");
S.labTest = rand()%100+1;
S.assignments = rand()%100+1;
S.exam = rand()%100+1;
afile.write(reinterpret_cast<char*>(&S),sizeof(S));
afile.close();

//Reading function
afile.open(fileName,ios::in|ios::binary);
afile.seekg(0,ios::end);
int nobyte = afile.tellg();
int recno = nobyte / sizeof(Student);
Student S;
//Loop and read every record
for(int i = 0;i<recno;i++)
{
afile.read(reinterpret_cast<char*>(&S),sizeof(S));
cout << "Name of Student: " << S.name << endl
<< "Lab mark: " << S.labTest << endl
<< "Assignment mark: " << S.assignments << endl
<< "Exam mark: " << S.exam << endl << endl;
}
afile.close();

你的代码有很多问题:

调用写入函数将永久覆盖上次写入的数据集。您必须添加:ios::append,以便新数据将写入您之前写入的最后一个数据后面。

使用afile.seekg(0,ios::end);移动以获取tellg文件大小后,必须先返回到文件的开头,然后才能使用afile.seekg(0,ios::beg)

看起来您使用 char 数组来存储字符串。这不是 c++ 风格!你如何使用它是危险的。如果使用 strcpy,则可以复制比为其保留的空间更长的字符串。所以你应该更喜欢std::string。但是你不能简单地写一个结构,std::string是二进制的!要获得检查副本,您可以使用strncpy,但这仍然不是c ++;)

对于第二个函数,每次 for 循环运行时,seekg 指针是否会自动移动以读取下一条记录?

是的,文件位置会移动,每个文件都会成功读取和写入。

通过简单地转储内存内容来写入二进制数据的一般评论: 这不是一个好主意,因为只有使用相同的计算机类型和相同的编译器选项,才能读回该数据。这意味着:具有不同字节序的机器将读取完全损坏的数据。此外,不同的整数类型(32位与64位)将破坏该代码!

因此,您应该花一些时间如何以可移植的方式序列化数据。有很多库可以用来读/写复杂的数据类型,如std::string或容器类型。

使用 SO 的提示: 请提供每个人都可以简单地剪切、粘贴和编译的代码。我不知道你的Student结构是什么。所以我做了很多假设!您的结构真的使用 char[] 吗?不知道!

#include <iostream>
#include <fstream>
#include <cstring>
const char* fileName="x.bin";
struct Student
{
char name[100]; // not c++ style!
int labTest;
int assignments;
int exam;
};
// Writing function   
void Write()
{
std::ofstream afile;
afile.open(fileName,std::ios::out|std::ios::binary|std::ios::app);
Student S;
strcpy(S.name,"test"); // should not be done this way! 
S.labTest = rand()%100+1;
S.assignments = rand()%100+1;
S.exam = rand()%100+1;
afile.write(reinterpret_cast<char*>(&S),sizeof(S));
afile.close();
}
void Read()
{
//Reading function
std::ifstream afile;
afile.open(fileName,std::ios::in|std::ios::binary);
afile.seekg(0,std::ios::end);
int nobyte = afile.tellg();
int recno = nobyte / sizeof(Student);
afile.seekg(0, std::ios::beg);
Student S;
//Loop and read every record
for(int i = 0;i<recno;i++)
{
afile.read(reinterpret_cast<char*>(&S),sizeof(S));
std::cout << "Name of Student: " << S.name << std::endl
<< "Lab mark: " << S.labTest << std::endl
<< "Assignment mark: " << S.assignments << std::endl
<< "Exam mark: " << S.exam << std::endl << std::endl;
}
afile.close();
}
int main()
{
for ( int ii= 0; ii<10; ii++) Write();
Read();
}

编辑。 显然,我的回应有点太晚了。Klaus编制了一个更好,更全面的回应,深入探讨了关于C式char []std::string和平台字节序的其他问题。

应追加到为每条记录打开的文件。在你的代码中,你根本没有这个。请以我们可以复制和粘贴并测试的方式编写代码。作为一个工作示例,您应该编写一些可以编译和运行的代码,如下所示:

#include <algorithm>
#include <cstring>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
// Student struct
struct Student {
char name[30];
float labTest;
float assignments;
float exam;
};
// Serializer
void serialize_student(const Student &s, const std::string &filename) {
// Append to the file, do not overwrite it
std::ofstream outfile(filename, std::ios::binary | std::ios::app);
if (outfile)
outfile.write(reinterpret_cast<const char *>(&s), sizeof(Student));
}
// Deserializer
std::vector<Student> deserialize_students(const std::string &filename) {
std::ifstream infile(filename, std::ios::binary);
std::vector<Student> students;
Student s;
while (infile.read(reinterpret_cast<char *>(&s), sizeof(Student)))
students.push_back(std::move(s));
return std::move(students);
}
int main(int argc, char *argv[]) {
// Generate records
std::vector<Student> mystudents;
std::generate_n(std::back_inserter(mystudents), 10, []() {
Student s;
std::strcpy(s.name, "test");
s.labTest = rand() % 100 + 1;
s.assignments = rand() % 100 + 1;
s.exam = rand() % 100 + 1;
return s;
});
// Print and write the records
for (const auto &student : mystudents) {
std::cout << student.name << ": [" << student.labTest << ','
<< student.assignments << ',' << student.exam << "].n";
serialize_student(student, "students.bin");
}
// Read and print the records
auto records = deserialize_students("students.bin");
std::cout << "===n";
for (const auto &student : records)
std::cout << student.name << ": [" << student.labTest << ','
<< student.assignments << ',' << student.exam << "].n";
return 0;
}