reading/writing class objects to files in c++ - c++

#include <iostream>
#include <fstream>
#include <stdlib.h>
using namespace std;
class Student{
private:
char name[40];
char grade;
float marks;
public:
void getdata();
void display();
};
void Student::getdata(){
char ch;
cin.get(ch);
cout<<"Enter name : ";
cin.getline(name,40);
cout<<"Enter grade : ";
cin>>grade;
cout<<"Enter marks : ";
cin>>marks;
cout<<"\n";
}
void Student::display(){
cout<<"Name : "<<name<<"\t";
cout<<"Grade : "<<grade<<"\t";
cout<<"Marks : "<<marks<<"\t"<<"\n";
}
int main(){
system("cls");
Student arts[3];
fstream f;
f.open("stu.txt",ios::in|ios::out|ios::binary);
if(!f){
cerr<<"Cannot open file !";
return 1;
}
for(int i=0;i<3;i++){
arts[i].getdata();
f.write((char*)&arts[i],sizeof(arts[i]));
}
f.seekg(0); //The question is about this statement;
cout<<"The contents of stu.txt are shown below : \n";
for(int j=0;j<3;j++){
f.read((char*)&arts[j],sizeof(arts[j]));
arts[j].display();
}
f.close();
return 0;
}
The above program reads and writes objects of Student from/to the file "stu.txt".
It runs fine. But it runs fine even if I switch off the fin.seekg(0) statement. I don't understand this part ? Are we not supposed to set the file pointer to the beginning of the file - before starting to read objects from the file(in the context of this particular program)?.

If you are at the end of the file, the read method call will fail, thus the Student structures are left untouched (so you are just printing again the same structures which you left untouched after writing them to file).

Related

Why is the data not getting properly written/read in this file?

The following code just accepts some data from the user in an object and writes it in a binary file. The first object gets written fine but after that it gets messed up.
Below is the output on reading the file after writing the first object onto it.
ROLL NO. NAME MARKS
234 Sansa 67
All fine and just as I expect. But when I append another object in the file, then this happens.
ROLL NO. NAME MARKS
234 SansSnow 78
564 Jon Snow 78
The roll numbers remain intact however names of the previous objects gets mixed up with the last object's and their marks get overwritten by the marks of the last object.
Why is it happening?
CODE:
#include <iostream>
#include <fstream>
#include <iomanip>
using namespace std;
fstream file;
class Student
{
protected:
int rollno;
char name[30];
float marks;
public:
void getData()
{
cout<<"ENTER THE DATA..."<<endl<<endl;
cout<<"Roll Number : ";
cin>>rollno;
fflush(stdin);
cout<<"Name : ";
cin.getline(name,30);
cout<<"Marks : ";
cin>>marks;
}
void displayData()
{
cout<<"\n"<<rollno;
cout<<setw(17)<<name;
cout<<setw(13)<<marks;
}
void writeData()
{
file.open("StudentData.dat", ios::app|ios::binary);
file.write((char*)this, sizeof(this));
file.close();
}
void readData()
{
file.open("StudentData.dat",ios::in|ios::binary);
while(file.read((char*)this, sizeof(this)))
{
displayData();
}
file.close();
}
};
Student S1;
int main()
{
cout<<"\nMAIN MENU"<<endl<<endl;
cout<<"1. Enter data and write to file\n";
cout<<"2. Read data from file and display\n";
cout<<"3. Exit\n\n";
cout<<"Enter your choice : ";
int choice;
cin>>choice;
switch(choice)
{
case 1:{
S1.getData();
S1.writeData();
cout<<endl<<endl;
cout<<"Data written to file successfully!"<<endl;
main();
break;
}
case 2:{
cout<<"ROLL NO."<<setw(10)<<"NAME"<<setw(15)<<"MARKS";
S1.readData();
cout<<endl;
main();
break;
}
case 3:{
exitop:
char confirmExit;
cout<<"\nAre you sure you want to exit? (Y/N) : ";
cin>>confirmExit;
if(confirmExit=='y' or confirmExit=='Y')
exit(0);
else if(confirmExit=='n' or confirmExit=='N')
{
cout<<"Exit Aborted.\n\n";
getchar();
main();
}
else{
cout<<"Invalid Input!";
goto exitop;
}
}
default:{
cout<<"Invalid Input!";
break;
}
}
return 0;
}
You’re only reading and writing 8 bytes. this is a pointer so sizeof(this) is 8 (on a 64 bit machine). What you actually want to do is get the size of the object instead so use sizeof(*this) or sizeof(Student).

My c++ program terminated without taking the input.What should i do?

The code below,works fine but it does not take any value for age and terminates.`
#include <iostream>
#include <string>
using namespace std;
class user{
int id,level=1,kills=0,age;
char name[20],server[40];
public:
void get(){
cout<<"Enter your name:";
cin>>name[20];
cout<<"Enter your age:";
cin>>age;
}
};
int main(){
user u;
u.get();
return 0;
}
/*Output
Enter your name:Jack
Enter your age:
C:\Users\user\documents\c++
*/
In the output section ,age is not accepted and the program terminates.
Use string name instead of char name[20] to take multi-character value. char name[20] will terminate after taking a single character.
Also, its valued will not be displayed on giving output.
Modified code for reference.
#include <iostream>
#include <string>
using namespace std;
class user{
int id,level=1,kills=0,age;
string name,server;
public:
void get(){
cout<<"Enter your name:";
cin>>name;
cout<<"Enter your age:";
cin>>age;
}
//test output
void put(){
cout<<name<<endl;
cout<<age<<endl;
}
};
int main(){
user u;
u.get();
//test
u.put();
return 0;
}
Just modify the code to this :
#include <iostream>
#include <string>
using namespace std;
class user{
int id,level=1,kills=0,age;
char name[20],server[40];
public:
void get(){
cout<<"Enter your name:";
cin>>name; // changes done here
cout<<"Enter your age:";
cin>>age;
}
};
int main(){
user u;
u.get();
return 0;
}
Job Done :)
Your problem is here:
cin>>name[20];
Why:
'name[20]' is 21th char of the array you defined before. It counts from 0! As this, it is simply a single char. If you now enter more than a single char, the rest is read by the cin>>age.
Example:
cout<<"Enter your name:";
cin>>name[20];
cout<<"Enter your age:";
cin>>age;
std::cout << "Name " << name << std::endl;
std::cout << "Age " << age << std::endl;
And entering:
Enter your name:1234
Enter your age:Name
Age 234
As you see, the '1' is now in the name and the rest is stored in age.
But attention: You defined your array as `name[20], which means you have 0..19 elements. Accessing name[20] is wrong!
But what you simply want to do was:
cin >> name;
The easiest way to handle strings (a long sequence of characters) or even the strings that have spaces just use the following library in C++.
#include <bits/stdc++.h>
Then just declare a string variable.
String name;
Now you can save a very long string without any error. e.g.
name = jshkad skshdur kslsjue djsdf2341;
and you'll get no error, enjoy ;)

dynamic initalisations of c++ objects based on user input

Hey sorry for the previous Question
OK..My project is to create and run a database for a college using c++
I have to use USN which is a Unique Student Number to access the database :
So i wrote the following program :
#include<iostream>
# include<conio.h>
#include<iomanip>
#include<string.h>
#include<stdlib.h>
int checkinarray(char[],char*);
using namespace std;
class student
{
private :
int sem;
float cgpa;
char password[11];
char passwordtrial[11];
void readdata();
void checkpassword();
void createpassword();
public :
char name[50];
int roll;
void printdata();
char USN[11];
static int number;
void opendatabase();
void firsttime();
public:
student(char n[50]="NONE")
{
number++;
roll=number;
cout<<endl<<"New record under the name of "<<n<<" has been created !"<<endl;
cout<<"Roll number set for new student as : "<<roll<<endl;
cout<<"Use this Roll number for further usage"<<endl<<endl;
};
};
void student::opendatabase()
{
int ch;
cout<<"Enter your name:"<<endl;
cin>>name;
cout<<"Enter your password"<<endl;
cin>>passwordtrial;
if(!strcmp(passwordtrial,password))
{
cout<<"Do you want to read or write";
cin>>ch;
switch(ch)
{
case 0 :
readdata();
break;
case 1 :
printdata();
break;
}
}
else
cout<<"Try Again";
};
void student::readdata()
{
cout <<endl<<"Enter the name of the student : ";
cin >> name;
cout <<endl<<"Enter the semester of the student : ";
cin >> sem;
cout <<endl<<" Enter the cgpa of the student : ";
cin >> cgpa;
};
void student :: printdata()
{
cout << "The name is : " << name << endl;
cout << "The sem is : " << sem << endl;
cout << "The roll is : " << roll << endl;
cout << "The cgpa is : " << cgpa << endl;
}
void student::firsttime()
{
cout<<"Enter your name :";
cin>>name;
cout<<"Hey "<<name<<" Welcome to DBMS "<<endl;
createpassword();
};
void student::createpassword()
{
cout<<"Please enter your 6 character password.. : ";
cin>>password;
cout<<"Please Input your Data here.... :" ;
readdata();
};
int student::number=0;
int main()
{
enum status {existing,newacc,exit};
enum functi{read,update};
char entry1[40];
char entry[10];
char usn[15];
char a[1000][15];
char n[40];
int i,j=0,k;
int s=2;
cout<<endl<<"WELCOME TO COLLEGE NAME"<<endl<<"Press enter to access Database...";
getch();
cout<<endl<<"Welcome to the Main Page of our Database : "<<endl;
while(1)
{//User option
cout<<endl<<"Do you want to access an old entry : "<<endl<<"OR"<<"Create a NEW entry : ";
cin>>entry1;
if(!strcmp(entry1,"old"))
s=existing;
else if(!strcmp(entry1,"new"))
s=newacc;
else
s=exit;
switch(s)
{
case existing:
{
i=1;
break;
}
case newacc:
{ i=1;
cout<<endl<<"Enter your usn : "<<endl;
cin>>usn;
strcpy(a[j],usn);
j++;
strcpy(n,usn);
cout<<n;
student usn(n);
usn.firsttime(); //Start here!! use i to go to next loop or stay in this loop,,change name entry to usn
break;
}
default :{cout<<"Error Input";i=0;break;}
}
if(i)
continue;
cout<<endl<<"What do u want to do??"<<endl<<"Read Entries "<<endl<<"Update entries";
cin>>entry;
if(!strcmp(entry,"read"))
s=read;
else if(!strcmp(entry,"update"))
s=update;
else
s=exit;
cout<<endl<<"Enter your usn : "<<endl;
cin>>usn;
if(checkinarray(a[15],usn))
{
switch(s)
{
case read:{
usn.printdata();
break;
}
case update:{
usn.firsttime();
break;
}
default :
cout<<"Are you sure you want to exit?"<<endl<<"Press 0 to exit"<<endl<<"to back to menu press 1";
cin>>k;
break;
}
if(!k)
break;
}
else cout<<"Invalid Roll number try again!";
}
}
int checkinarray(char a[][15],char b[])
{
int len;
//Finding the length of the string :
len=(sizeof(a)/sizeof(a[0]));
//Checking Conditions for roll number:
for(int k=0;k<len;k++)
{
if(strcmp(a[k],b))
return 1;//stringcompare!!
}
return 0;
}
okay so when i run this i get the following error :
request for member 'printdata' in 'usn', which is of non-class type 'char [15]'
AND
request for member 'firsttime' in 'usn', which is of non-class type 'char [15]'
So please help me overcome this error by suggesting different ways to create and call objects based on user input
OP's problem can be reduced to the following example:
#include<iostream>
#include<string.h>
using namespace std;
class student
{
public:
student(char n[50])
{
}
};
int main()
{
char usn[15];
char n[40];
{
cin >> usn;
strcpy(n, usn);
student usn(n);
}
usn.printdata();
}
This is what is meant by a Minimal, Complete, and Verifiable example. Not everything, but everything needed to reproduce the problem. The beauty of the MCVE is it has reduced the problem to the point where all of it can fit on the screen and probably within the brain, making it easy to analyze and test.
Things to note:
There are two variables named usn
char usn[15];
is an automatic variable within main. It is visible only within main and will expire at the end of main
student usn(n);
is an automatic variable within an anonymous block within main. It is visible only within this block and will expire at the end of the block.
Annotating this block to better explain what is happening, we get
{
cin >> usn; // uses char usn[15];
strcpy(n, usn);
student usn(n); // declares a new variable named usn that replaces char usn[15];for the remainder of this block
} // student usn(n); ends right here and is destroyed.
usn.printdata(); //uses char usn[15]; which has no printdata method.
So how do we fix this?
student usn(n); must have a wider scope.
one of these two variables must change names because once student usn(n); has wider scope it will collide with char usn[15];
Lets give that a quick try.
int main()
{
char usn[15];
char n[40];
student stud(n);
{
cin >> usn;
strcpy(n, usn);
}
stud.printdata();
}
Isn't possible because there is no data in n with which we can make stud. At least not for this minimal example.
int main()
{
char usn[15];
char n[40];
student * stud;
{
cin >> usn;
strcpy(n, usn);
stud = new student(n);
}
stud->printdata();
delete stud;
}
Solves that by dynamically allocating stud so that it is no longer scoped by the braces. Unfortunately that does pick up some extra memory management woes. stud must be deleted. Added a delete, but what if printdata throws an exception and delete stud; is skipped?
int main()
{
char usn[15];
char n[40];
std::unique_ptr<student> stud;
{
cin >> usn;
strcpy(n, usn);
stud.reset(new student(n));
}
stud->printdata();
}
std::unique_ptr takes care of that. But... What about that whole database thing? Why not store stud in a list?
int main()
{
char usn[15];
char n[40];
std::vector<student> studs;
{
cin >> usn;
strcpy(n, usn);
studs.emplace_back(n); // or studs.push_back(student(n));
}
for (student & stud: studs)
{
stud.printdata();
}
}
std::vector solves both problems at once.

File handling using classes c++

I'm doing an assignment which requires file handling in C++.Assignment is long enough to be posted here, but i have made a similar simple code to elaborate my question.The following code is not read all data from file it only read the last record
please help me to make it correct.
#include <iostream>
#include <conio.h>
#include <fstream>
#include <string.h>
//class
class medicine{
int id;
char med_name[50];
int price;
int quantity;
char date[50];
public:
void data_entery(){
//int i;char na[50];int p;int q;char da[50];
cout<<"Enter id: ";
cin>>id;
cout<<"Enter name: ";
cin>>med_name;
cout<<"Enter price: ";
cin>>price;
cout<<"Enter quantity: ";
cin>>quantity;
cout<<"Enter exp_date: ";
cin>>date;
}
void display(){
cout<<this->id<<"\t "<<this->med_name<<"\t "<<this->price<<"\t"<<this- >quantity<<"\t "<<this->date;
}
}med[10];
using namespace std;
int main (){
//writing in file
ofstream wfile("medicines.txt",ios::app|ios::binary);
wfile.seekp(0,ios::end);
int rec=wfile.tellp()/sizeof(medicine);
cout<<"there are "<<rec<<" records";
med[rec].data_entery();
wfile.write(reinterpret_cast<char *>(&med),sizeof(medicine));
wfile.close();
//reading in file
ifstream rfile("medicines.txt",ios::in|ios::binary);
rfile.seekg(0,ios::end);
rec=rfile.tellg()/sizeof(medicine);
rfile.seekg(0,ios::beg);
cout<<"there are "<<rec<<" records";
for (int k=0;k<rec;k++){
med[k].display();
}
rfile.read(reinterpret_cast<char *>(&med),sizeof(med)*rec);
rfile.close();
getch();
return 0;
}
I am assuming that your file medicines.txt is initially empty.
What you have done wrong is that you have made 10 objects of the medicine class, but you are writing only one object to the file.
What you need to do here is:
for (int i=0; i < 10; i++) {
med[i].data_entery();
wfile.write(reinterpret_cast<char *>(&med),sizeof(medicine));
}
wfile.close();
Now you'll need to add 10 medicines, after details of each medicine has been added, that medicine will be written to the file.
I hope this helps.
Here is a better version of your code
#include <iostream>
#include <conio.h>
#include <fstream>
#include <string.h>
using namespace std;
//class
class medicine
{
int id;
char med_name[50];
int price;
int quantity;
char date[50];
public:
void data_entery()
{
//int i;char na[50];int p;int q;char da[50];
cout<<"\nEnter id: ";
cin>>id;
cout<<"Enter name: ";
cin>>med_name;
cout<<"Enter price: ";
cin>>price;
cout<<"Enter quantity: ";
cin>>quantity;
cout<<"Enter exp_date: ";
cin>>date;
}
void display()
{
cout<<"\nID :"<<this->id<<"\nName :"<<this->med_name<<"\nPrice :"<<this->price<<"\nQuantity :"<<this->quantity<<"\nExp Date :"<<this->date<<"\n";
}
}med;
int main ()
{
//writing in file
ofstream wfile("medicines.txt",ios::app|ios::binary);
wfile.seekp(0,ios::end);
int rec=wfile.tellp()/sizeof(medicine);
cout<<"\nthere are "<<rec<<" records";
med.data_entery();
wfile.write(reinterpret_cast<char *>(&med),sizeof(medicine));
wfile.close();
//reading in file
ifstream rfile("medicines.txt",ios::in|ios::binary);
rfile.seekg(0,ios::end);
rec=rfile.tellg()/sizeof(medicine);
rfile.seekg(0,ios::beg);
cout<<"\nthere are "<<rec<<" records";
while(rfile.read(reinterpret_cast<char *>(&med),sizeof(med)))
{
med.display();
}
rfile.close();
getch();
return 0;
}
Now, when you are using files, you do not need to create an array of objects. Once you store the object into the file, you can just reuse that object. The data can be accessed from the File. That is what I have done in this code. I have edited this code in such a manner as to not change your method, instead just corrected the problems and also made your code a little better. In each run of this code, you enter the details of one record, and then the details of all the records are shown.
The good thing about this code is, you can enter as many records as you want. You are not restricted to 10 records like your original code.

Searching in binary file C++

#include <iostream>
#include <fstream>
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
class Student{
private:
char name[40];
char grade;
float marks;
public:
void getdata();
void display();
char* getname(){return name;}
void search(fstream,char*);
};
void Student::getdata(){
char ch;
cin.get(ch);
cout<<"Enter name : ";
cin.getline(name,40);
cout<<"Enter grade : ";
cin>>grade;
cout<<"Enter marks : ";
cin>>marks;
cout<<"\n";
}
void Student::display(){
cout<<"Name : "<<name<<"\t";
cout<<"Grade : "<<grade<<"\t";
cout<<"Marks : "<<marks<<"\t"<<"\n";
}
void search(fstream fin,char* nm)/*initializing argument 1 of 'void search(std::fstream, char*)'*/{
Student s;
fin.open("stu.txt",ios::in|ios::binary);
while(!fin){
fin.read((char*)&s,sizeof(s));
if(s.getname()==nm){
cout<<"Record found !";
s.display();
break;
}
}
fin.close();
}
int main(){
system("cls");
char nam[40];
Student arts[3];
fstream f;
f.open("stu.txt",ios::in|ios::out|ios::binary);
if(!f){
cerr<<"Cannot open file !";
return 1;
}
for(int i=0;i<3;i++){
arts[i].getdata();
f.write((char*)&arts[i],sizeof(arts[i]));
}
f.close();
cout<<"Enter name to be searched for : ";
cin.getline(nam,40);
char* p = new char[40];
p=nam;
search(f,p);/*synthesized method 'std::basic_ios<char>::basic_ios(const std::basic_ios<char>&)' first required here*/
getch();
f.close();
return 0;
}
The above program first creates a file "stu.txt" and writes user-given input to the file.
It is then supposed to search for a record based on the name the user enters ( using the search() function ). I'm getting errors when calling search() and also defining search(). I've put in the errors the compiler throws as comment lines. Could anyone explain what's going wrong in there ?
fstream is not copyable, so you have to pass fstream as a reference, or in c++11, move it.
Given you access f after calling search, it is best to pass it by reference.
Change your function to accept the fstream as a reference:
void search(fstream& fin,char* nm)