File object being overwritten to 0 while reading - c++

I have a class wallet which has info like name,pin,amount etc. I use function create() to create new users and write it to a file wallet.txt. This all process is fine. Problem arises when I transfer amount from one to other. Using function pay(). Here s,s1,s2 are objects of class wallet. File is opened in read mode and sender's info and receiver info is searched and stored in s1 and s2. Then fin is closed and fout is opened in write mode and things are written into file.
void pay()
{
char f1='n',f2='n';
int pos1,pos2;
int ycode;
float amount;
cout<<"\nenter your pin: ";
cin>>pin;
cout<<"\nenter the code of the receiver: ";
cin>>ycode;
ifstream fin("wallet.txt",ios::in);
while(fin.read((char*)&s,sizeof(s)))
{
if(ycode==s.getcode())
{
pos1=fin.tellg();
f1='f';
}
if(pin==s.getpin())
{
pos2=fin.tellg();
f2='f';
}
};
if(f1=='f' && f2=='f')
{ cout<<"Enter the amount to be transferred: ";
cin>>amount;
fin.seekg(pos1-sizeof(s));
fin.read((char*)&s1,sizeof(s));
s1.add(amount); //function of class wallet::add(float amount){balance+=amount}
fin.seekg(pos2-sizeof(s));
fin.read((char*)&s2,sizeof(s));
s2.sub(amount); //function of class wallet similar to add()
fin.close();
ofstream fout("wallet.txt",ios::ate);
fout.seekp(pos1-sizeof(s));
fout.write((char*)&s1,sizeof(s1));
s1.display();
fout.seekp(pos2-sizeof(s));
fout.write((char*)&s2,sizeof(s2));
s2.display();
fout.close();
}
else
cout<<"\nInvalid E-code or E-pin";
}
My file content content for receiver become
Name: [empty]
balance: [amount]
pin: 0
code: 0
and receiver
Name: [empty]
balance: -[amount]
pin: 0
so basically amount became 0 and then transfer occurs. WHY?
code: 0
class wallet{
int ecode;
int epin;
float balance;
char name[23];
public:
void create(); //function to open new E wallet
void add(float); //function to add money in wallet
void sub(float);//function to withdraw money from wallet
int getpin(); //function to return pin
int getcode(); //function to return code
void show(); //function to show details
void display(); //function to display current balance
}s,s1,s2 ; //class ends here
void wallet::display()
{
cout<<"\n The current balance of "<<name<<" is: "<<balance;
}
Also I tried debugging by calling the below function in every line of pay(), and found that problem occurred after first display().
void viewall()
{
ifstream fin("wallet.txt",ios::in);
while(fin.read((char*)&s,sizeof(s)))
{
s.show();
cout<<endl;
};
fin.close();
}

Related

Problem while reading objects from file in c++

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!
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;
}
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];
}

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.

trying to develop code for user login in c++

I am working on a project in which i have to set up user login. User data is stored in a file in a class structure.
This is my function for login:
bool check_login(int n, string pass)
{
bool found = false;
account ac;
ifstream infile;
infile.open("final.dat",ios::binary);
if(!infile)
{
cout<<"File could not be open !! Press any Key...";
return false;
}
while(!infile.eof() && found==false)
{
infile.read(reinterpret_cast<char *> (&ac), sizeof(account));
if(ac.retacno()==n)
{
if(pass.compare(ac.retpassword())==0)
found=true;
return found;
}
}
infile.close();
return found;
}
class account
{
int acno;
string password;
string name;
string address;
char sex;
string phonenumber;
public:
void create_account(); //function to get data from user
void show_account() const; //function to show data on screen
void modify(); //function to add new data
void withdraw(int,int); //function to accept amount and subtract from balance amount
void donate(int,int); //function to accept amount and add to balance amount
void report() const; //function to show data in tabular format
int retacno() const; //function to return account number
string retpassword() const; //function to return password
};
This poses a runtime error - process exited with return value 255. in dev-c++
This here
infile.read(reinterpret_cast<char *> (&ac), sizeof(account));
will not work well for a class. You need to add code to serialize and deserialize your account instances. As it is now you read in all the stuff from the file into the account instance without running any constructors of the members e.g. string.
To serialize/deserialize you can for instance override the >> and << operators.

How do i input strings with spaces?

I want to input a string (with spaces in between) and i am using cin.getline(). But i am getting a run time error (loop executes infinitely).
class account
{
int acno;
int password;
char name[50];
char address[100];
char sex;
string phonenumber;
public:
void create_account(); //function to get data from user
void show_account() const; //function to show data on screen
void modify(); //function to add new data
void withdraw(int,int); //function to accept amount and subtract from balance amount
void donate(int,int); //function to accept amount and add to balance amount
void report() const; //function to show data in tabular format
int retacno() const; //function to return account number
int retpassword() const; //function to return password
}; // class definition
this is my function for inputting the data for a class record.
void account::create_account()
{
cout<<"\nEnter The account No. :";
cin>>acno;
cout<<"\n\nEnter The Name of The account Holder : ";
cin.getline(name,49);
cin.ignore();
cout<<"\n\nEnter your Password (Max 8 characters) : ";
cin>>password;
cin.ignore();
cout<<"\n\nEnter your Address : ";
cin.getline(address,99);
cin.ignore();
cout<<"\nEnter your Contact Number: ";
cin>>phonenumber;
cin.ignore();
cout<<"\nSex (Enter M for male and F for female): ";
cin>>sex;
cout<<"\n\n\nAccount Created..\n\n";
}
When I execute this I get a run time error if I introduce spaces in between string entries
and it skips the string input if I don't use cin.ignore() function.
Use the free-standing std::getline function, not the member function. The former operates on a std::string, so you don't have to mess around with a raw pointer.

Unhandled exception at 0x5DF9CCC8

when i run this code everything goes fine when writing , but when i press 2 to read it goes well and read everything just fine but when it it finishes reading the file (show function) it pops up a problem says " Unhandled exception at 0x5DF9CCC8 " with break , continue options .
from my searching i think the problem comes from a pointer points to Null , but i don't know how to resolve it .
here is the code
class person{
public :
string name;
int id ;
int age ;
void person :: show();
void person :: add_menue();
void person :: insert_data();
void person :: show_data();
};
person personobject;
void person :: add_menue()
{
cout << "Please Enter Name ID Age :" << endl;
cin >> name >> id >> age ;
}
ofstream file;
void person::show()
{
cout<<"Age => "<<age<<endl;
cout<<"Name => "<<name<<endl;
cout<<"ID => "<<id<<endl;
}
void person :: insert_data()
{
ofstream file;
file.open("binary.dat",ios::app| ios::binary);
personobject.add_menue();
file.write((char*)&personobject, sizeof(personobject));
file.close();
}
void person :: show_data()
{
ifstream file;
file.open("binary.dat",ios::in| ios::binary);
if(!file)
{
cout<<"File not Found";
}
else
{
file.read((char*)&personobject, sizeof(personobject));
while(!file.eof())
{
personobject.show();
cout<<"Press Any Key....For Next Record"<<endl;
getchar();
file.read((char*)&personobject, sizeof(personobject));
}
}
file.close();
}
void main ()
{
int choice;
cout << "1 - to write \n2 - to read" << endl;
cin >> choice;
if (choice==1)
{
person f;
f.insert_data();
}
if (choice==2)
{
person a;
a.show_data();
system ("pause");
}
}
Because you are using a string object instead of a plain character array, you can't just type cast the whole class to a char* and write it to the file (which in itself is a poor way of doing it. What if you changed around the order of the parameters and tried loading an older file?).
In your insert_data function, write each variable individually instead of casting the entire class. You could write the age and id first and you know that would take up 8 bytes, so whatever is remaining is the size of the name which can be loaded back into the string object in your read_data function.