george sobhy
george sobhy

Reputation: 23

fstream::write and fstream::read changing both reading and writing pointers

I am learning about how to write records on file and read it .
I created a class called student , the class has function like enterStudent , showStudent , printInsideFile (student info) ,...... .
when I try to write student info into the file , it works .
when I try to read all student info from the file , it works
when I try to do both , something unexpected happens (nothing show up)

i thought that the problem with file.flush() , so when i deleted it the output was unreadable text
i could close the file and open it again but i think that is silly
the code is:

#include <iostream>
#include <string>
#include <fstream>


using namespace std;

class student {
private:
    char id[5], name[20], age[5], address[50], gender[5];
public:
    void enterStudent() {
        cout << "Enter student id : "; cin >> id;
        cout << "\nEnter student name : "; cin >> name;
        cout << "\nEnter student age : "; cin >> age;
        cout << "\nEnter student address : "; cin >> address;
        cout << "\nEnter student gender : "; cin >> gender;

    }
    void showStudent() {
        cout << "#########student data##########\n";
        cout << "student id : "<< id;
        cout << "\nstudent name : " << name;
        cout << "\nstudent age : " << age;
        cout << "\nstudent address : " << address;
        cout << "\nstudent gender : " << gender<<endl;
    }
    void printInsideFile(fstream &file) {
        file.write(id,sizeof(id));
        file.write(name, sizeof(name));
        file.write(age, sizeof(age));
        file.write(address, sizeof(address));
        file.write(gender, sizeof(gender));
        file.flush();
    }
    bool readFromFile(fstream &file) {
        if (file.eof())
            return 0;
        file.read(id, sizeof(id));
        file.read(name, sizeof(name));
        file.read(age, sizeof(age));
        file.read(address, sizeof(address));
        file.read(gender, sizeof(gender));
        if (file.eof())
            return 0;
        return 1;
    }
    void showAllFromFile(fstream &file) {
        while (this->readFromFile(file)) 
            this->showStudent();
    }
};

int main() {
    student s;
    fstream file;

    file.open("a.txt", ios::in | ios::out | ios::app);
    if (!file)
        goto k270;
    
    s.enterStudent();
    s.printInsideFile(file);

    //when i read one student , it works okay

    //s.readFromFile(file);
    //s.showStudent();

    //when i try to read multiple students , it doesn't work at all
    s.showAllFromFile(file);




    file.close();


k270:
    system("pause");
    return 0;
}

Upvotes: 1

Views: 462

Answers (2)

george sobhy
george sobhy

Reputation: 23

The problem was not the ios::app

It seems that functions file.write() and file.read() change either reading pointer and writing pointer .
Even if you use file<<"text"; or file>>array of chars; both of pointers are changing .

I searched about it but didn't find explanation but i find the code of ostream::write and istream::read ,they were advanced so if anybody check the link and tell us why

note that ,when the reading pointer points at the end of the file , it will print out nothing

Upvotes: 1

Scheff&#39;s Cat
Scheff&#39;s Cat

Reputation: 20141

Considering that I have least experience with std::ios::app, I became curious to find out how it actually works.

I made the following MCVE on coliru which (I believe) is working as expected by the OP:

#include <fstream>
#include <iostream>

int main()
{
  // open a text file
  std::fstream file("test.txt", std::ios::in | std::ios::out | std::ios::app);
  // remember current position of read head
  file.seekg(0, std::fstream::end); // a trick to update the read head before writing anything
  std::fstream::pos_type pos0 = file.tellg();
  std::cout << "pos0: " << pos0 << '\n';
  // write a line to end of text
  std::cout << "Write a line.\n";
  file << "A line written by main.cpp" << std::endl;
  std::cout << "pos after writing: " << file.tellg() << '\n';
  // rewind to remembered position of read head
  file.seekg(pos0);
  // read the last written line
  { std::string buffer; std::getline(file, buffer);
    std::cout << "Last line: '" << buffer << "'\n";
  }
  // rewind to the beginning of file
  file.seekg(0);
  // read all
  std::cout << "Read all:\n";
  for (std::string buffer; std::getline(file, buffer);) {
    std::cout << "'" << buffer << "'\n";
  }
}

Test Session:

g++ -std=c++17 -O2 -Wall -pedantic -pthread main.cpp \
  && echo -e "1st line written by echo\n2nd line written by echo\n3rd line written by echo" > test.txt \
  ; ./a.out
pos0: 75
Write a line.
pos after writing: 102
Last line: 'A line written by main.cpp'
Read all:
'1st line written by echo'
'2nd line written by echo'
'3rd line written by echo'
'A line written by main.cpp'

Notes:

  1. I was fully sure about the file.seekg(0) which is needed before read all to rewind the read file head to the begin of file.

  2. I was not sure about the read the last written line
    I found that the file.seekg(pos0); is necessary.
    Obviously, the previous write seems to touch the write head (of course) as well as the read head.

  3. I had some trouble to retrieve the proper read head position using std::istream::tellg(). It returned the expected value after the first file output but not before.
    My last resort was a file.seekg(0, std::fstream::end); to move the read head to end of file before anything is written.

  4. I apologize for the total lack of any error checking in the example.
    Of course, this has to be added in a serious application.
    I left it out to keep the sample code as minimal as possible.
    Additionally, I didn't consider the error handling a problem in OPs sample code.

Upvotes: 1

Related Questions