Search code examples
c++filefstreambinaryfilesseek

Getting wrong output using seekg and seekp


I have created a class Student.
The Student class:

class Student
{
    friend ostream& operator<<(ostream& os, const Student& s)
    {
        return os <<
            "Roll no.: " << s.roll_no << '\n' <<
            "Name: " << s.name << '\n' <<
            "Phone no.: " << s.phone_no << '\n' <<
            "Address: " << s.address << '\n';
    }
public:
    Student() = default;
    Student(int r, const char* n, int p_no, const char* a):
        roll_no(r), phone_no(p_no)
    {
        strcpy(name, n);
        strcpy(address, a);
    }
    int get_roll() const
    {
        return roll_no;
    }
private:
    int roll_no;
    char name[40 + 1];
    int phone_no;
    char address[100 + 1];
};

In the StudentList class, I am storing some Student objects in a binary file.

class StudentList
{
public:
    StudentList(const string& fname):
        filename(fname)
    {
        make_index();
    }
    Student get_student(int roll)
    {
        fstream ifs(filename, ios::in | ios::binary);
        int pos = index[roll];
        ifs.seekg(pos * sizeof(Student));
        Student s;
        ifs.read(reinterpret_cast<char*>(&s), sizeof(s));
        return s;
    }
    void change_student(Student s)
    {
        fstream ofs(filename, ios::out | ios::binary);
        int pos = index[s.get_roll()];
        ofs.seekp(pos * sizeof(s));
        ofs.write(reinterpret_cast<const char*>(&s), sizeof(s));
    }
    void add_student(Student s)
    {
        fstream ofs(filename, ios::out | ios::binary | ios::app);
        ofs.write(reinterpret_cast<const char*>(&s), sizeof(s));
        int total_no = index.size() + 1;
        index[s.get_roll()] = total_no - 1;
    }
private:
    string filename;
    map<int, int> index;
    void make_index()
    {
        fstream ifs(filename, ios::in | ios::binary);
        int pos = 0;
        if (ifs.is_open()) {
            while (!ifs.eof()) {
                Student s;
                ifs.read(reinterpret_cast<char*>(&s), sizeof(s));
                index[s.get_roll()] = pos;
                pos++;
            }
        }
    }
};

However, this code is giving the wrong output. The binary file is not present when I am running the program. So StudentList::make_index creates a new file when it is called in the constructor.

int main()
{
    StudentList sl("student.dat");
    sl.add_student(Student(14, "V", 12, "14/14"));
    sl.add_student(Student(1, "A", 12, "13/13 Tollygunge"));
    cout << sl.get_student(14) << '\n';
    cout << sl.get_student(1) << '\n';
    sl.change_student(Student(1, "B", 12, "14/14, Bosepukur Road"));
    cout << sl.get_student(14) << '\n';
    cout << sl.get_student(1) << '\n';
    return 0;
}

Output:

Roll no.: 14
Name: V
Phone no.: 12
Address: 14/14

Roll no.: 1
Name: A
Phone no.: 12
Address: 13/13 Tollygunge

Roll no.: 0
Name: 
Phone no.: 0
Address: 

Roll no.: 1
Name: B
Phone no.: 12
Address: 14/14, Bosepukur Road

Trying to debug with gdb, I have found out that the function StudentList::change_student(Student) is not working correctly. In the function, despite using seekp to set the write position after the first Student object in the file(as pos = 1), the first Student object is also somehow getting modified.
Edit:
I think I have found the error. Changing this line in StudentList::change_student(Student s):

fstream ofs(filename, ios::out | ios::binary);

to:

fstream ofs(filename, ios::out | ios::binary | ios::in);

gives the correct output.
Output:

Roll no.: 14
Name: V
Phone no.: 12
Address: 14/14

Roll no.: 1
Name: A
Phone no.: 12
Address: 13/13 Tollygunge

Roll no.: 14
Name: V
Phone no.: 12
Address: 14/14

Roll no.: 1
Name: B
Phone no.: 12
Address: 14/14, Bosepukur Road

Most probably as Aumnayan suggested, the previous opening mode was erasing the contents of the file in the change_student function.


Solution

  • Try adding the ios::app flag in the change_student method. I seam to remember that ios::out is destructive, which would leave you with a file with student(1) propagated and student 14 set to (presumably) 0. It's been a while since I played with this type of file io tho, so YMMV.