0

I am learning c++ and have a trouble in file handling. I am writing a code as a homework where i have to write objects into a file and then read those objects as array from the file at once. Here is my code:

#include <iostream>
#include <fstream>

using namespace std;

class Records{
    char* name;
    int roll;
public:
    Records()
    {
        name = new char[20];
    }
    void setData()
    {
        cout<<"Enter name: "<<endl;
        cin>>name;
        cout<<"Enter roll"<<endl;
        cin>>roll;
    }
    char* getname()
    {
        return name;
    }
    int getRoll()
    {
        return roll;
    }
    void operator = (Records& no)
    {
        name = no.name;
        roll = no.roll;
    }
};

int main()
{
    int i =0 ;
    Records rec;
    rec.setData();
    Records::increase();
    ofstream fout;
    fout.open("file.txt", ios::app);
    fout.write((char*)&rec, sizeof(rec));
    fout.close();

    Records* results = new Records[20];
    Records rec1;
    ifstream fin;
    fin.open("file.txt", ios::in);
    while(!fin.eof())
    {
        fin.read((char*)&rec1, sizeof(rec1));
        results[i] = rec1;
        i++;
    }
    fin.close();
    cout<<results[0].getRoll();
    return 0;
}

So basically, I made a Records class and store its object in a file. That works fine but I faced problem while taking data from file. It is not showing anything or sometimes showing garbage value. Anyone have better idea please hep me. Thanks in advance!

Sachin Bhusal
  • 63
  • 2
  • 10
  • 1
    Do you have to use raw pointers? Why not use `std::string`? Your class is very unsafe as-is because of the `char* name` - and it leaks memory because you don't `delete[]` the pointer. Read [rule of 3/5/0](https://en.cppreference.com/w/cpp/language/rule_of_three). Also read: [Why is `iostream::eof()` inside a loop condition (i.e. `while (!stream.eof())`) considered wrong?](https://stackoverflow.com/questions/5605125/why-is-iostreameof-inside-a-loop-condition-i-e-while-stream-eof-cons) – Ted Lyngmo Nov 26 '20 at 16:08
  • 2
    You can't store pointers in a file and use them later. You need some kind of serialization. – molbdnilo Nov 26 '20 at 16:12
  • 1
    what you are doing is rather low level, you don't have to go that low. Using the `ofstream`s `operator<<` is much simpler if you just want to write a string and a number per line into a text file – 463035818_is_not_an_ai Nov 26 '20 at 16:14

2 Answers2

1

First, you have to open file in binary mode for read and write.

 std::ofstream fou("out_filename",std::ofstream::binary);
 std::ifstream fin("in_filename", std::ifstream::binary);

Secondly, you assign operator=() is problematical. It assigns two records using the same address. Therefore in the reading process, all 20 elements in result[i] were all sharing the address of rec1::name. You have to correct the operator=() by copying contents of name.

This is not good.

void operator = (Records& no)
{
    name = no.name;
    roll = no.roll;
}

Rewrite as follows:

Edit: since your objects are all initially assigned with its memory. The new allocation is not necessary.

Records& Records::operator=(const Records& no)
{
   // this->name = new char [20]; 
    std::copy_n(no.name, 20, this->name); // include <algorithm>
    roll = no.roll;
    return *this; // return current object for another =.
}

Finally, add a destructor

Records::~Records() {
delete [] this->name; }

Good luck!

ytlu
  • 412
  • 4
  • 9
0

After fixed some other errors, I post this final version for you reference. Note that this project cannot use dynamic allocation for the field "name". Using dynamic allocation, the 20-byte of "name" is not counted as the size of class Records, and the pointer itself is not transferable. It causes read/write error in the field "name".

#include <iostream>
#include <fstream>
#include <algorithm>
using namespace std;

class Records{
    char name[20];
    int roll;
public:
    Records()
    {
      //  name = new char[20];
    }
    void setData()
    {
        cout<<"Enter name: "<<endl;
        cin>>name;
        cout<<"Enter roll"<<endl;
        cin>>roll;
    }
    const char* getname() const
    {
        return name;
    }
    int getRoll() const
    {
        return roll;
    }
    Records& operator = (const Records& no)
    {
        std::copy_n(no.name, 20, this->name);
        roll = no.roll;
        return *this;
    }
};
int main()
{
    int i =0, c ;
    std::string a;
    Records rec;
    ofstream fout;
    fout.open("file.txt", std::ofstream::binary);
    c = 0;
    while (1)
    {
       std::cout << "Input record [" << c << "] ? (y/n) ";
       std::cin >> a;
       if (a[0]=='y' || a[0]=='Y')
        {
          rec.setData();
          fout.write((char*)&rec, sizeof(rec));
          ++c;
        }
        else break;
    }
    fout.close();
// output 
    Records* results = new Records[20];
    Records rec1;
    ifstream fin;
    fin.open("file.txt", std::ifstream::binary);
    while(!fin.eof())
    {
       fin.read((char*)&rec1, sizeof(rec1));
       results[i] = rec1;
       i++;
    }
   fin.close();
   // eidt to print all records
   for (int j=0; j<(i-1); j++)
    {  std::cout << "record # = " << j << std::endl;
       std::cout << "   name = " << results[j].name;
       std::cout << "   roll = " << results[j].roll << std::endl;
    }

   return 0;
}

A test run

$ ./a.exe
Input record [0] ? (y/n) y
Enter name:
aaaa
Enter roll
1234
Input record [1] ? (y/n) y
Enter name:
bbbb
Enter roll
2345
Input record [2] ? (y/n) y
Enter name:
cccc
Enter roll
3456
Input record [3] ? (y/n) n
1234
ytlu
  • 412
  • 4
  • 9
  • Thank you sir! But i have another question, how to display all the records at once as we don't how big our array is? – Sachin Bhusal Nov 27 '20 at 03:19
  • @SachinBhusal During the process of reading, the variable i is the number of records fetched. But due to the while(end of file condition), there is one extra record with data duplicate of the last one. Please look up my edited part for a demo. – ytlu Nov 27 '20 at 14:03
  • @SachinBhusal Yes. After a run, you can go to the directory and change the file.txt to another name, e.g. filesave.txt. Then rewrite your program, remark the first part (input section), and directly go to the second part: open filesave.txt and read from it. – ytlu Nov 27 '20 at 17:11
  • When i read from file then it is showing in a binary form like it was saved in a file. I mean it is not showing in proper "english" readable text. Why? – Sachin Bhusal Nov 28 '20 at 12:35
  • @SachinBhusal Sure. You had saved it in binary form, and have to open it also in binary form. You cannot open this kind of file in notepag, or read it as a text file. >>> You may open the file.txt append mode, which allow you to continue write new records into the end of the file. – ytlu Nov 28 '20 at 15:29
  • @SachinBhusal if you want a file od text format, you have to open it in binary mode, read from it. And then write out to a ios::out file, using txtfile << name; txtfile << roll;. Thus created a txt file which can be read from text editor. – ytlu Nov 28 '20 at 15:33
  • Also, why it is printing in binary form whenever I want to read from file? Its so confusing! – Sachin Bhusal Nov 28 '20 at 15:43
  • I have opened in (binary + append) mode while writing and only in (binary) mode while writing into file, but not printing proper output, why? – Sachin Bhusal Nov 28 '20 at 15:46
  • this is my problem-https://stackoverflow.com/questions/65056564/problem-while-reading-objects-from-file-in-c – Sachin Bhusal Nov 29 '20 at 02:53