简体   繁体   中英

Reading or writing binary file incorrectly

The output of the code show gibberish values for all the variables of the Student struct. When the display function is ran.

I've include the relevant code in each of the add and display function for the binary file.

For the second function, does the seekg pointer automatically move to read the the next record each time the for loop runs?

//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();

There are a lot of problems with your code:

Calling your write function will permanently overwrite the last written data set. You have to add: ios::append , so that new data will be written behind the last data you wrote before.

After you move with afile.seekg(0,ios::end); to get with tellg the file size, you have to go back to the start of the file before reading with afile.seekg(0,ios::beg)

It looks that you use a char array to store a string. This is not c++ style! And it is dangerous how you use it. If you use strcpy, you can copy a string which is longer than the space you reserved for it. So you should prefer std::string for that. But you can't simply write a struct which constains std::string as binary! To get checked copy you can use strncpy , but that is still not c++ ;)

For the second function, does the seekg pointer automatically move to read the the next record each time the for loop runs?

Yes, the file position moves which each successful read and write.

A general remark writing binary data by simply dumping memory content: That is not a good idea, because you can only read that data back, if you use the same machine type and the same compiler options. That means: A machine with different endianness will read data totally corrupted. Also a different integer type ( 32 bit vs 64 bit ) will break that code!

So you should invest some time how to serialize data in a portable way. There are a lot of libraries around which can be used to read/write also complex data types like std::string or container types.

A hint using SO: Please provide code which everybody can simply cut and paste and compiled. I did not know what your Student struct is. So I take a lot of assumptions! Is your struct really using char[]? We don't know!

#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();
}

EDIT. Apparently, I was a bit too late in responding. Klaus has compiled a better, more comprehensive response dwelling into other problems regarding C-style char [] , std::string and the endianness of the platform.

You should append to the file opened for every record. In your code you don't have this, at all. Please write the code in a way we can copy and paste, and test. As a working example, you should write some code that can be compiled and run as below:

#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;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM