0

I am doing a small college project where I have to add, edit and search records in/from file using OOP concept. Adding into file is working fine but whenever I try to read from file it is printing in unreadable texts. Here is full code and output. main.cpp

#include <iostream>
#include <cstdlib>
#include <fstream>
#define MIN 20
#define MAX 100
#include "student.h"

using namespace std;

void add_student();
void edit_student();
void search_student();
void addToFile(const Student&);
Student* fetchFromFile();
int getFileSize();
void updateFile(Student*);

// Student stud[MAX];
int main()
{
  int choice;
  system("cls");                                                  
  system("Color B0");
  while(1)
  {                                                                                            
    cout<<"\n\t\tWhat do you want to do?"<<endl;
    cout<<"\t\t----------------------"<<endl;                                                 
    cout<<"\t\t1-Add student"<<endl;                                                       
    cout<<"\t\t2-Edit student"<<endl;                                                         
    cout<<"\t\t3-Search student"<<endl;                                                   
    cout<<"\t\t4-Quit Program"<<endl;                                                              
    cout<<"\t\t----------------------"<<endl;            
    cout<<"Enter your choice: ";                           
    cin>>choice;                                           
    switch(choice)                                         
    {
      case 1:
        add_student(); //calling add_student function to add records.
        break;
      case 2:
        edit_student();
        break;
      case 3:
        search_student();
        break;
      case 4:
        return 0;
        break;
      default:
        cout<<"Invalid choice";
        break;
    }
}
  return 0;
}

int Student::id = getFileSize() - 1; //Initialize id equals to size of file

// setData function of class Student definition
void Student :: setData()
{
  // taking input from user
  cout<<"Enter student roll no in format(1XXX): ";
  cin>>roll;
  cout<<"Enter student name: ";
  cin>>name;
  cout<<"Enter stduent date of birth(dd/mm/yy): ";
  cin>>dob;
  cout<<"Enter stduent phone no: ";
  cin>>phone;
  cout<<"Enter student address: ";
  cin>>address;
  stdId = Student::id;
}

void Student :: showData()
{
  cout<<stdId<<"  ";
  cout<<roll<<"   ";
  cout<<name<<"     ";
  cout<<dob<<"\t";
  cout<<phone<<"   ";
  cout<<address<<"\n\n";
}

const int Student :: getRoll()
{
  return roll;
}

Student& Student::operator = (const Student& newObj)
{
  stdId = newObj.stdId;
  roll = newObj.roll;
  name = newObj.name;
  dob = newObj.dob;
  phone = newObj.phone;
  address = newObj.address;
  return *this;
}

void add_student()
{
  Student stud;
  Student::incrementId();
  stud.setData();
  addToFile(stud); //adding records to file
  system("CLS");
  cout<<endl;
  cout<<"--------------------------------------------------------------------------------"<<endl;
  cout<<"---------------------------Student updated record Table---------------------------------"<<endl;
  cout<<"--------------------------------------------------------------------------------"<<endl;
  cout<<"ID      "<<"Roll      "<<"Name      "<<"DOB      "<<"Phone no         "<<"Address\n\n";
  cout<<"--------------------------------------------------------------------------------"<<endl;

  Student* student = fetchFromFile(); //getting records from file in array of objects
  int length = getFileSize(); //getting length of array of objects

  for(int i=0; i<(length-1); i++)
  {
    student[i].showData(); //showing all the data
  }

  cout<<"--------------------------------------------------------------------------------"<<endl;
  cout<<"---------------------------------FINISH-----------------------------------------"<<endl;
  cout<<"--------------------------------------------------------------------------------"<<endl;
  cout<<"You want to add more?(Y/n):  ";
  char c;
  cin>>c;
  if(c=='y' || c=='Y')
  {
    add_student();
  }
  else{
    system("pause");
  }
}

void edit_student(){
  //Showing existing record first before editing
  cout<<endl;
  cout<<"--------------------------------------------------------------------------------"<<endl;
  cout<<"---------------------------Student Existing record Table---------------------------------"<<endl;
  cout<<"--------------------------------------------------------------------------------"<<endl;
  cout<<"ID   "<<"Roll   "<<"Name      "<<"DOB      "<<"Phone no         "<<"Address\n\n";
  cout<<"--------------------------------------------------------------------------------"<<endl;

  Student* student = fetchFromFile(); //fetching all records from file
  int length = getFileSize();

  for(int i=0; i<(length-1); i++)
  {
    student[i].showData();
  }
  int idnumber;
  cout<<"--------------------------------------------------------------------------------"<<endl;
  cout<<"Which ID number your want to edit: ";

  cin>>idnumber;            //Asking the user at which ID he wants to make a change.
  //checking for valid id number
  if(idnumber>length || idnumber<0)
  {
    cout<<"\nInvalid ID Number."<<endl;
  }
  //showing existing information about that specific record
  cout<<"\nExisted information about this record.\n\n";
  cout<<"--------------------------------------------------------------------------------"<<endl;
  cout<<"ID   "<<"Roll   "<<"Name      "<<"Father\tCell no.      "<<"DOB          "<<"Address\n\n";
  cout<<"--------------------------------------------------------------------------------"<<endl;
  student[idnumber].showData();
  cout<<"\n\nEnter new data for above shown record.\n\n";
  student[idnumber].setData();         //Inputting data for that specific record.
  updateFile(student);
  cout<<"\n\nRecord updated successfully."<<endl;
  cout<<endl;
  cout<<"--------------------------------------------------------------------------------"<<endl;
  cout<<"---------------------------Updated record Table---------------------------------"<<endl;
  cout<<"--------------------------------------------------------------------------------"<<endl;
  cout<<"ID   "<<"Roll   "<<"Name      "<<"DOB      "<<"Phone no         "<<"Address\n\n";
  cout<<"--------------------------------------------------------------------------------"<<endl;
  for(int i=0; i<(length-1); i++) //Showing updated record Table
  {
    student[i].showData();
  }
}

void search_student(){
  Student* student = fetchFromFile();
  int fileLenth = getFileSize() - 1;
  int searchkey;
  cout<<"Enter roll_no of student you want to search: ";
  cin>>searchkey;     //roll_no as the search key can be entered by user.
  for(int i=1; i<fileLenth; i++)
  {
    if(searchkey==student[i].getRoll()) //checking for roll no
    {
      student[i].showData();
    }
  }
  cout<<"--------------------------------------------------------------------------------"<<endl;
  cout<<"---------------------------------FINISH-----------------------------------------"<<endl;
  cout<<"--------------------------------------------------------------------------------"<<endl;
  system("pause");
}

//FILE HANDLING

void addToFile(const Student& obj)
{
  ofstream fout;
  fout.open("records.txt", std::ofstream::app | std::ofstream::binary);
  fout.write((char*)&obj, sizeof(obj));
  cout<<"Added to file successfully!"<<endl;
  fout.close();
}

Student* fetchFromFile()
{
  int i=0;
  Student obj;
  Student* returnObj = new Student[MAX];
  ifstream fin;
  fin.open("records.txt", std::ifstream::binary);
  while(!fin.eof())
  {
    fin.read((char*)&obj, sizeof(obj));
    returnObj[i] = obj;
    i++;
  }
  fin.close();
  delete[] returnObj;
  return returnObj;
}

int getFileSize()
{
  int i=0;
  Student obj;
  ifstream fin;
  fin.open("records.txt", std::ifstream::binary);
  while(!fin.eof())
  {
    fin.read((char*)&obj, sizeof(obj));
    i++;
  }
  fin.close();
  return i;
}

void updateFile(Student* student)
{
  ofstream fout;
  fout.open("records.txt", std::ofstream::binary);
  fout.write((char*)&student, sizeof(student));
  fout.close();
}

student.h header file

// A student class that hold students attributes like id, name, address and class
// Object of this class will be craeted to store student details
class Student
{
  int stdId;
  int roll;
  std::string name;
  std::string dob;
  std::string phone;
  std::string address;
public:
  static int id; //we will increase 'id' whenever student is added to the record
  //Member functions declaration
  void setData(); //this function will take input from user and set the data to attributes of class
  void showData(); //This function will give student data to user when called
  static void incrementId()
  {
    id++;
  }
  const int getRoll();
  Student& operator = (const Student&);
};

Sample output 1

When I add a student object to file

Sample output 2

Reading all records from file

Problem1: Showing garbage value of id.

Sample output 3

Adding another object to file

Sample output 4

Reading all objects from file

Sample output 5

Now went back and chose to edit record

Problem2: See how the records are printing in unreadable form.

Why is this happening. Now if I close program and run it again then it is still showing in unreadable text. Hope you get my issue. I want to have detailed explanation about this. Also, if I have done other miscellaneous mistakes, please let me know. Thank you!

Sachin Bhusal
  • 63
  • 2
  • 10
  • If Student is not a POD type `fin.read((char*)&obj, sizeof(obj));` is not valid. You can not read or write this way. – drescherjm Nov 29 '20 at 02:52
  • 1
    There is a great duplicate that covers this problem that I just can't find right now. What is comes down to a `string` at it's simplest is a pointer to a `char` array and an integer. Write a `string` to a file and you write the pointer, not the stuff the pointer points at. That pointer is useless to you because when you read the file back, either the `string` isn't in memory anymore and the pointer points to someone else's memory OR you have two copies of the `string` and when one copy goes out of scope it takes the `char` array with it and the other copy is now a timebomb. – user4581301 Nov 29 '20 at 03:02
  • @user4581301 so what should I do to overcome that. – Sachin Bhusal Nov 29 '20 at 03:28
  • 1
    What you should have done is experiment a bit to see where the problem lies (i.e. with your string members) and use that information to greatly reduce your code to a [mre]. Compare the complexity of your code example with that in [debug read/write string to binary file](https://stackoverflow.com/questions/20896825/) and [How to write std::string to file?](https://stackoverflow.com/questions/15388041/) – JaMiT Nov 29 '20 at 03:52
  • 2
    Does this answer your question? [How to write std::string to file?](https://stackoverflow.com/questions/15388041/how-to-write-stdstring-to-file) – JaMiT Nov 29 '20 at 03:52
  • 2
    [You need to serialize the data](https://isocpp.org/wiki/faq/serialization). The easiest thing to do is to abandon `read` and `write`. Most of your data is string data, so write `<<` and `>>` overloads for `Student` and store everything as text. Stick a comma or something between each member so you can handle names and other members that contain spaces or other delimiters that will screw up reading the data back. – user4581301 Nov 29 '20 at 04:43
  • I just realized that my earlier comment might have come across harsh. That was not my intent; apologies if it was misconstrued. My intent was along the lines of "Your question could (and should) have been better written (here's how), but I'll overlook that and point you to existing answers that should help." – JaMiT Nov 29 '20 at 05:20
  • 1
    **Look for inspiration into the source code of existing open source C++ projects** on [github](http://github.com/) or [gitlab](http://gitlab.com/), maybe into [RefPerSys](http://refpersys.org/) or [SWIG](http://swig.org/) or [Clang](http://clang.llvm.org/). Read the documentation of your compiler (e.g. [GCC](http://gcc.gnu.org/) ...) and debugger (e.g. [GDB](https://www.gnu.org/software/gdb/)...) – Basile Starynkevitch Nov 29 '20 at 15:49

2 Answers2

2

Student is too complicated to read with unformatted IO like read and write. In technical terms, it is not Trivially Copyable because it contain std::strings, and string is not Trivially Copyable.

The easiest thing to do is to abandon read and write. Most of your data is string data, so write << and >> overloads for Student and store everything as text.

    friend std::ostream & operator<<(std::ostream & out, 
                                     const Student & stu) 
    { 
        out << stu.stdId << ',' <<stu.roll << ','<< stu.name... <<'\n'; 
        return out;
    } 

Reading the data back in is a bit trickier

    friend std::istream & operator>>(std::istream & in, 
                                     Student & stu) 
    { 
        std::string line;
        if (std::getline(in, line))
        {
            std::stringstream strm(line);
            if (!(strm >> stu.stdId >> comma >> stu.roll >> comma) ||
                !std::getline(strm, stu.name, ',') ||
                !std::getline(strm, stu.dob, ',') ||
                ...))
            { // If any read failed, mark the stream as failed so the caller knows.
                out.setstate(std::ios_base::failbit);
            }
        }
        return out;
    } 
user4581301
  • 33,082
  • 7
  • 33
  • 54
1

First, a minor trick, add a empty constructor for class to initailial all members, integers{0}, and string{}, which will elminiate some unwanted gabages.

 Student::Student() :stdId(0), roll(0), name{}, dob{}, phone{}, address{} {;}

Your major problem arised from the function fetchfomrfile() where you delete the fetched Student array, therefore the caller received a undefined data array:

Student* fetchFromFile()
{
  int i=0;
  Student obj;
  Student* returnObj = new Student[MAX];

  //reading records from file
  fin.close();
  delete[] returnObj;// <<<< youe deleted
  return returnObj; // and return it as undefined
}

Since you are able to calculate the size of file, I suggest in the caller function:

void search_student(){
 // Student* student = fetchFromFile();
    Student *student = new Stduent [getFileSize()];
     fecchFromFile(student); // use this array in fetch_file 
  // other things
 }

Rewrite fetchFromFile as :

viod fetchFromFile(Stduent *ss)
{
  // read file data to array ss[i];
}
ytlu
  • 412
  • 4
  • 9