Binary files with Struct, classes, fstream error - c++

i have a question regarding a assignment i have.
here i have 2 of the classes, employee class and gm class
void GM::addEmployee(fstream& afile, int noOfRecords)
{
afile.open("EmployeeInfo.dat", ios::in | ios::binary);
employee::eInfo e;
employee emp;
char name[80];
cout << "\nAdd Employee Info" << endl;
cout << "---------------------" << endl;
cout << "New Employee Username: ";
cin.clear();
cin.ignore(100, '\n');
cin.getline(name, 80);
//Check if there is already an entry inside the file with this name.
//If yes, add fail
bool flag = true;
if(noOfRecords > 0)
{
for(int i=1; i<=noOfRecords; i++)
{
afile.read (reinterpret_cast <char *>(&e), sizeof(e));
if(!strcmp(name, e.username))
{
cout << "Username is used, add GM failed" << endl;
flag = false;
}
}
}
afile.close();
if(flag)
{
//open in appending mode
afile.open("EmployeeInfo.dat", ios::out | ios::app | ios::binary);
strcpy(e.username, name);
cout << "Please Enter New Employee's Password: ";
cin.getline(e.password, 80);
cout << "\nPlease Enter New Employee's Appointment "
<< "\n(0 = GM / 1 = HM / "
<< "2= BS / 3 = FOS)\n : ";
cin >> e.eid;
cin.clear();
cin.ignore(100, '\n');
emp.dist = strlen(e.password);
emp.caesar_encrypt(e.password, 3, emp.dist);
afile.write(reinterpret_cast <const char *>(&e), sizeof(e));
afile.close();
cout << "\nEmployee Added" << endl;
}
}
The above is a function from my GM class, which is to add employees.
i have declared a structure in employee class as
struct eInfo
{
char username [80];
char password [80];
int eid;
};
The problem with this way of doing, is that when i try to add employee
my EmployeeInfo.dat data disappears. everything becomes blank after i used the add employee function.
can anyone guide me on what i did wrong?

This is the wrong way to read data into e:
afile.read(reinterpret_cast<char*>(&e), sizeof(e));
Likewise, this is the wrong way to write data from e:
afile.write(reinterpret_cast<const char*>(&e), sizeof(e));
If you need to print or read the data members of e, you need to do so one at a time. Moreover, using read/write in this context is unecessary because you simply use the extractor and inserter:
afile >> e.username;
// ...
afile << e.username << e.password;

Related

how to display records sorted according to roll no in c++ .dat file

This is all my code I want to sort the records when display all students using option 2 in the main menu
this code returning me the data as it was saved means unsorted.
#include<iostream>
#include<fstream>
#include<iomanip>
#include<vector>
using namespace std;
class student
{
int Idnum;
char Name[25];
char Course[30];
int Result;
public:
void getdata();
void showdata() const;
void show_tabular() const;
int getIDNum() const;
};
void student::getdata()
{
cout << "\nEnter student's ID Number: ";//prints Enter student's ID Number
cin >> Idnum;
cout << "\n\nEnter student's Name: ";//prints Enter student's Name
cin.ignore();//to ignore from the input buffer
cin.getline(Name, 25);
cout << "\nEnter student's Course: ";//prints Enter student's Course
cin >> Course;
cout << "\nEnter student's Result: ";//prints Enter student's Result
cin >> Result;
}
void student::showdata() const
{
cout << "\nID Number: " << Idnum;//prints ID Number
cout << "\nName: " << Name;//prints Name
cout << "\nCourse: " << Course;//prints Course
cout << "\nResult: " << Result;//prints Result
}
void student::show_tabular() const
{
cout << Idnum << setw(6) << " " << Name << setw(20) << Course << setw(20) << Result << setw(4) << endl;
}
int student::getIDNum() const
{
return Idnum;
}
void SaveStudent();
void displayAll();
void Searchdisplay(int);
void modifyStudent(int);
void deleteStudent(int);
void DisplayResult();
This is the code for creating a student.dat file if it does not exist here and if will exist then write a new student record but this will not sort the records as I want.
void write_student()//to create students record
{
student st;
ofstream outFile;
ifstream inFile;
outFile.open("student.dat", ios::binary | ios::app);//opens file student.dat
st.getdata();
//cout << st.getIDNum();
if (inFile.seekg(reinterpret_cast<char *> (&st)))
{
}
outFile.write(reinterpret_cast<char *> (&st), sizeof(student));//writes the file
outFile.close();//closes the file
cout << "\n\nStudent record Has Been Created ";
cin.ignore();
cin.get();
}
This is the place where I am showing my student's records but this will show unsorted records and I want it in a sorted way means ascending order by roll no of students.
void display_all()//to display the student record
{
student st;
ifstream inFile;
inFile.open("student.dat", ios::binary);//opens file student.dat
if (!inFile)
{
cout << "File could not be open !! Press any Key...";
cin.ignore();//to ignore from the input buffer
cin.get();//to access the char
return;
}
cout << "\n\n\n\t\tDISPLAY ALL RECORD !!!\n\n";
while (inFile.read(reinterpret_cast<char *> (&st), sizeof(st)))
{
st.showdata(); //function to show data on screen
cout << "\n\n====================================\n";
}
inFile.close();//closes the file
cin.ignore();
cin.get();
}
void display_sp(int n)//to search for student record
{
student st;
ifstream inFile;
inFile.open("student.dat", ios::binary);//opens file student.dat
if (!inFile)
{
cout << "File could not be open !! Press any Key...";
cin.ignore();
cin.get();
return;
}
bool flag = false;//for false condition
while (inFile.read(reinterpret_cast<char *> (&st), sizeof(student)))
{
if (st.getIDNum() == n)
{
st.showdata();
flag = true;
}
}
inFile.close();//closes the file
if (flag == false)
cout << "\n\nrecord not exist";//prints record not exist
cin.ignore();
cin.get();
}
void modify_student(int n)//to modify the record
{
bool found = false;//for false condition
student st;
fstream File;
File.open("student.dat", ios::binary | ios::in | ios::out);//opens the file student.dat
if (!File)
{
cout << "File could not be open !! Press any Key...";
cin.ignore();
cin.get();
return;
}
while (!File.eof() && found == false)
{
File.read(reinterpret_cast<char *> (&st), sizeof(student));//reads the file
if (st.getIDNum() == n)
{
st.showdata();
cout << "\n\nPlease Enter The New Details of student" << endl;
st.getdata();
int pos = (-1)*static_cast<int>(sizeof(st));
File.seekp(pos, ios::cur);
File.write(reinterpret_cast<char *> (&st), sizeof(student));
cout << "\n\n\t Record Updated";
found = true;
}
}
File.close();//closes the file
if (found == false)
cout << "\n\n Record Not Found ";
cin.ignore();
cin.get();
}
void delete_student(int n)//to delete the student record
{
student st;
ifstream inFile;
inFile.open("student.dat", ios::binary);//opens the student.dat file
if (!inFile)
{
cout << "File could not be open !! Press any Key...";
cin.ignore();
cin.get();
return;
}
ofstream outFile;
outFile.open("Temp.dat", ios::out);//to open another file
inFile.seekg(0, ios::beg);
while (inFile.read(reinterpret_cast<char *> (&st), sizeof(student)))
{
if (st.getIDNum() != n)
{
outFile.write(reinterpret_cast<char *> (&st), sizeof(student));
}
}
outFile.close();//to open another file
inFile.close();
remove("student.dat");
rename("Temp.dat", "student.dat");
cout << "\n\n\tRecord Deleted ..";
cin.ignore();
cin.get();
}
int main()
{
char ch;
int num;
cout.setf(ios::fixed | ios::showpoint);
cout << setprecision(2);
do
{
system("cls");
cout << "\t===================================";
cout << "\n\n\t1. CREATE STUDENT RECORD";
cout << "\n\n\t2. DISPLAY ALL STUDENTS RECORDS";
cout << "\n\n\t3. SEARCH STUDENT RECORD ";
cout << "\n\n\t4. MODIFY STUDENT RECORD";
cout << "\n\n\t5. DELETE STUDENT RECORD";
cout << "\n\n\t6. EXIT";
cout << "\n\n\t===================================";
cout << "\n\n\tPlease Enter Your Choice (1-6): ";
cin >> ch;
system("cls");
switch (ch)//uses switch casee
{
case '1': write_student(); break;
case '2': display_all(); break;
case '3': cout << "\n\n\tPlease Enter Student's ID number: "; cin >> num;
display_sp(num); break;
case '4': cout << "\n\n\tPlease Enter Student's ID number: "; cin >> num;
modify_student(num); break;
case '5': cout << "\n\n\tPlease Enter Student's ID number: "; cin >> num;
delete_student(num); break;
case '6': exit(0);;
default: cout << "\a";
}
} while (ch != '6');
return 0;
}
The main difficulty with showing students in sorted order is that your program works by having every function read a file and operate on each student record sequentially. Sorting can only happen when you have all of the data loaded into your program. In order to display a sorted list of students, you need to load all of the student records into your program and put them all in the same container. A std::vector<student> is the simplest container to work with here.
So, I think the overall flow of your program should work like this:
Read the entirety of student.dat and put all student information found there in a container like std::vector<student>.
Sort the container.
Let the user manipulate the records (create, read, update, delete) by modifying the members of the std::vector<student>.
When the user wants to exit, write the contents of the std::vector<student> back to the file students.dat (possibly with an option to quit without saving).
In the next section, suggestions 1 and 2 answer your question while suggestion 3 tells how the rest of your program will need to be changed to work with the first two suggestions.
Some suggestions to do this:
Make it easier to load a student from a file.
Create a method for the student class that takes a std::ifstream& as an argument. That way, after you open a file, you can load all of the student data like this:
int main()
{
ifstream inFile("student.dat");
std::vector<student> all_students;
while(inFile)
{
student st;
st.getData(inFile);
all_students.push_back(st);
}
// continue with program
}
This method will be very similar to the current student::getData() method, but it will skip the text prompts. I also find this way much easier to understand than your reinterpret_cast code that makes me worry about what it actually does. Is student.dat human readable in your current code?
Create a comparison function bool operator<(const student& A, const student& B) for sorting.
This function should return true if the A student should be sorted before B student and false otherwise. Once this method is created, then you can call std::sort(all_students.begin(), all_students.end()) to sort the student list by the criteria defined in that comparison function. The std::sort() function will need to be called after every modification to the student list.
All functions that manipulate student data should get passed the all_students data
It is much easier to work with the student data directly through purpose-written methods rather than doing complicated manipulations of filestream data. For example, if you passed std::vector<student>& to the modify_student() function, it would look like this:
void modify_student(std::vector<student>& all_students, int n)//to modify the record
{
for (auto& st : all_students)
{
if (st.getIDNum() == n)
{
st.showdata();
cout << "\n\nPlease Enter The New Details of student" << endl;
st.getdata();
return;
}
}
cout << "\n\n Record Not Found ";
}
If you are not working in C++11 or later, this is equivalent:
void modify_student(std::vector<student>& all_students, int n)//to modify the record
{
for (size_t i = 0; i < all_students.size(); ++i)
{
student& st = all_students[i];
if (st.getIDNum() == n)
{
st.showdata();
cout << "\n\nPlease Enter The New Details of student" << endl;
st.getdata();
return;
}
}
cout << "\n\n Record Not Found ";
}
This is much simpler since you don't have file-traversal and user input/output hiding the actual logic of the function.
If a function is only displaying data like display_all(), then you should pass the student list as const std::vector<student>& all_students.
There's much more to be done to improve this program, but I think these suggestions should get you started thinking in a more organized way.

Updating information in a binary text file using struct (C++)

I'm writing a program to store inventory data in a binary text file. One of the functions is to search for the name of an item (that was already entered) within the text file, and if found, read the information to a structure, update it, and send the structure back to the file.
The trouble is using seekp/seekg to overwrite the correct data.
I.E. The structure holds the item name, total stock, the Wholesale price, and the Retail price. But my current code will only overwrite the item name when sent back to the text file.
I have a feeling this is due to either using seek/read/write incorrectly.
In the following code I only use one struct to temporarily hold data when inputting/outputting to the file. (I hate to dump code like this but I'm not sure what else to do at this point.) Below is the function to modify a specific item that has already been entered:
The structure is
struct inventory
{
string itemname;
int stock;
double costWS,
costRE;
};
but i've only created one named "holder", to use when adding items to the text file or searching/modifying items.
void ModifyRecord()
{
fstream update ("Records.txt" , ios::in|ios::ate|ios::binary);
bool found = false;
long int whereAmI;
string tomodify;
cout << "Enter the name of the item to modify." << endl;
cin >> tomodify;
while ( !update.eof() || !found )
{
//holder is the structure containing inventory data
//this will read only one structure at a time, to send to holder
update.read(reinterpret_cast<char *> (&holder), sizeof(holder));
cout << "Reading new struct." << endl;
whereAmI = update.tellg();//save input position
//check this structure for the name entered
if (holder.itemname.find(tomodify) == 0)
{
found = true;//exit while loop
update.seekg( -( sizeof(holder) ), ios::cur);
//Update data
cout << "String found. \nYou have selected to modify: "
<< holder.itemname << endl;
cout << "Enter new name for the item: " << endl;
cin >> holder.itemname;
do
{
cout << "Enter amount stocked." << endl;
cin >> holder.stock;
}while(holder.stock < 0);
do
{
cout << "Enter wholesale cost." << endl;
cin >> holder.costWS;
}while(holder.costWS < 0);
do
{
cout << "Enter retail cost." << endl;
cin >> holder.costRE;
}while(holder.costRE < 0);
//send back to file
update.write(reinterpret_cast<char *> (&holder), sizeof(holder));
whereAmI = update.tellp();//save current output position
update.close();
break;
}
}
if ( !found )
cout << "Keyword not found." << endl;
}
So how do I overwrite the correct data using seekg/p? Am I using read/write correctly?
First: "binary text file" sounds like "cold hot water". You should decide: do you want to work with binary or text file.
Second: Your struct inventory contains a field of type string, that's why it cannot be used like a POD, but you're trying to do that:
update.seekg( -( sizeof(holder) ), ios::cur);
^^^^^^^^^^^^^^
and
update.write(reinterpret_cast<char *> (&holder), sizeof(holder));
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^
Third: If you'll change your struct like this:
struct inventory {
enum { MAX_ITEM_LENGTH = 100 };
char itemname[ MAX_ITEM_LENGTH + 1 ]; // +1 for trailing '\0'
int stock;
double costWS,
costRE;
};
then the algorithm would be like this:
fstream update ("Records.txt" , ios::in|ios::ate|ios::binary);
bool found = false;
string tomodify;
cout << "Enter the name of the item to modify." << endl;
cin >> tomodify;
while ( !update.eof() )
{
update.read( reinterpret_cast<char *> (&holder), sizeof(holder) );
if ( tomodify == holder.itemname )
{
update.seekg( -( sizeof(holder) ), ios::cur);
cout << "String found. \nYou have selected to modify: "
<< holder.itemname << endl;
cout << "Enter new name for the item: " << endl;
string newItemName;
cin >> newItemName;
strncpy( holder.itemname, newItemName.c_str(), inventory::MAX_ITEM_LENGTH );
do
{
cout << "Enter amount stocked." << endl;
cin >> holder.stock;
} while(holder.stock < 0);
do
{
cout << "Enter wholesale cost." << endl;
cin >> holder.costWS;
} while(holder.costWS < 0);
do
{
cout << "Enter retail cost." << endl;
cin >> holder.costRE;
} while(holder.costRE < 0);
//send back to file
update.write(reinterpret_cast<char *> (&holder), sizeof(holder));
found = true;
break;
}
}
if ( !found )
cout << "Keyword not found." << endl;
}

how to read from a binary file and search for a record

I am trying to read from a file and search a particular record based on the employee number entered. I have written the code but every time i search for a record which is already present i am getting the message record not found. Can anyone please point out the error.
My code is:
#include <iostream>
#include <fstream>
using namespace std;
class emp
{
int empno;
char name[20];
char dept[10];
float salary;
public:
void getdata()
{
cout << "Enter the employee number " << endl;
cin >> empno;
cout << "Enter the name : " << endl;
cin >> name;
cout << "Enter the department of the employee : " << endl;
cin >> dept;
cout << "Enter the salary of the employee : " <<endl;
cin >> salary;
}
void display()
{
cout << "Emp No : " <<empno;
cout << endl << "Name : " << name << endl << "Department : " <<dept <<endl
<<"Salary : " << salary <<endl;
}
int getempno()
{
return empno;
}
};
int main()
{
emp obj1;
int eno;
char ch = 'n';
ifstream file1("emp.txt", ios:: in); // this file should already exist
cout << "Enter the employee number to be searched for : " <<endl;
cin >> eno;
while(!file1.eof())
{
file1.read((char *)&obj1, sizeof(obj1));
if(obj1.getempno()==eno)
{
obj1.display();
ch = 'y';
break;
}
}
if(ch =='n')
cout << "Record Not Found !!" << endl;
file1.close();
}
I am using a variable eno in my main function and comparing the eno to the empno returned from the function getempno. If it is equal i am calling the member function display but the display function is not working. I am only getting the message record not found.
Open the stream as binary as said in the title:
ifstream file1("emp.txt", ios:: in | ios::binary); // binary
and also change your loop in order to not test on eof() without having read first:
while (file1.read((char *)&obj1, sizeof(obj1)))
I could test successfully this updated code, by producing a quick and dirty binary file, written with ios::binary set (I don't put the constructor code here):
void produceTest(string file) {
ofstream os(file, ios::out | ios::binary);
emp a(1, "Durand", "IT", 1234.30);
emp b(2, "Dupond", "Finance", 1530.20);
emp c(25, "Chris", "MD", 15.30);
os.write(reinterpret_cast<char*>(&a), sizeof(emp));
os.write(reinterpret_cast<char*>(&b), sizeof(emp));
os.write(reinterpret_cast<char*>(&c), sizeof(emp));
}
If it doesn't work, the problem is with your file. Potential issues could for example be:
the file was written without ios::binary, producting alteration of the structure (ignoring 0, on windows tranforming binary bytes 0x0A into binary 0x0D + binary 0x0A)
the file was written on a system with a different int encoding (big endian vs.little endian
the file was written with a leading unicode BOM
the encoding of the file is not as you thought.

Questions about fstream only reading in 1 structure

I have the following problem: my program that uses fstream doesn't seem to be able to write more than 1 data struct into my .dat file.
Below is my code, I've only been able to add 1 set of data. When I tried to add another, it worked but did not write out. Any idea why?
For example, I run this function, type in the details, and successfully entered 1 set of booking information, and info is stored in my .dat file.
When I re-run this function, type in the details, the 2nd set of booking information is NOT recorded in my .dat file. Therefore my .dat file ends up having only the first entry, and not storing the subsequent entries
void booking::bkForm(fstream& afile, bForm& b)
{
customer c;
GM g;
GM::holidayPackages h;
char fname [30];
char lname [30];
char address [50];
char date [30];
char req [100];
int pid, position, choice;
bool iStat = false;
bool dStat = false;
int noOfRecords = getNoBFRecords(afile);
int q = g.getNoOfHRecords(afile);
cout << "Please Fill Up Required Booking Form." << endl;
cout << "Please enter your first name: ";
cin.clear();
cin.ignore(100, '\n');
cin.getline(fname, 30);
cout << "Please enter your last name: ";
cin.getline(lname,30);
cout << "Please enter your address: ";
cin.getline(address,50);
cout << "\nThese are the available Packages to choose from." << endl;
g.printHolidayPackages(afile, h);
afile.open("holidayPackages.dat", ios::in | ios::binary);
while(iStat == false)
{
cout << "\nPlease enter the Package ID of the tour that you want to join." << endl;
cout << "Package ID: ";
cin >> pid;
for(int i = 0; i < q; i++)
{
afile.read (reinterpret_cast <char *>(&h), sizeof(h));
if(pid == h.holID)
{
iStat = true;
position = i;
}
}
if(iStat == false)
{
cout << "ID not found, please enter valid Package ID" << endl;
}
}
while(choice!=1 && choice!=2)
{
afile.seekg ((position) * sizeof (GM::holidayPackages), ios::beg);
afile.read (reinterpret_cast <char *>(&h), sizeof (h));
cout << "\nPleasse choose the Date of tour that you want to join." << endl;
cout << "1) " << h.holDate1 << endl;
cout << "2) " << h.holDate2 << endl;
cout << "Your choice: ";
cin >> choice;
if(choice == 1)
{
strcpy(date, h.holDate1);
}
else
{
strcpy(date, h.holDate2);
}
}
cout << "\nPlease State Any Special Requirement That You Have Below." << endl;
cin.clear();
cin.ignore(100, '\n');
cin.getline(req, 100);
afile.close();
afile.open("bookingInfo.dat", ios::out | ios::app | ios::binary);
strcpy(b.bfName, fname);
strcpy(b.blName, lname);
strcpy(b.bAddress, address);
strcpy(b.bDate, date);
strcpy(b.bStatus, "Unchecked");
strcpy(b.bReq, req);
b.packageID = pid;
srand(time(NULL));
int a = rand () % 100000+899990; // random 6 digit number as booking ref.
for(int k = 0; k < noOfRecords; k++)
{
afile.read (reinterpret_cast <char *>(&b), sizeof(b));
while(a == b.bookingRef)
{
a = rand () % 100000+899990;
}
}
b.bookingRef = a;
cout << "Booking Submitted." << endl;
cout << "Please take down the booking reference no : " << b.bookingRef << endl;
cout << "You will need the reference number to check the status of your booking" << endl;
afile.write(reinterpret_cast <const char *>(&b), sizeof(b));
afile.close();
}
Tthis is some sample output. There is only 1 result instead of a few.
==========================================================
Details Of Booking Number: 0
===========================================================
Booking Reference no: 966373
Customer Name: Cheryl Tan
Customer Address: St24 Tampines
Package ID of Package Booked: 9102
Package Date Choosen: 02032014
Special Requirements: none
Booking Status: Unchecked
its okay, i realise my error.
the error being i called for read, but i've only opened the file for write/append.
corrected code fragment will be like this..
afile.open("bookingInfo.dat", ios::in | ios::binary); //changed here to read
srand(time(NULL));
int a = rand () % 100000+899990; // random 6 digit number as booking ref.
for(int k = 0; k < noOfRecords; k++)
{
afile.read (reinterpret_cast <char *>(&b), sizeof(b));
while(a == b.bookingRef)
{
a = rand () % 100000+899990;
}
}
afile.close(); // close read
afile.open("bookingInfo.dat", ios::out | ios::app | ios::binary); // open append
strcpy(b.bfName, fname);
strcpy(b.blName, lname);
strcpy(b.bAddress, address);
strcpy(b.bDate, date);
strcpy(b.bStatus, "Unchecked");
strcpy(b.bReq, req);
b.packageID = pid;
b.bookingRef = a;
cout << "Booking Submitted." << endl;
cout << "Please take down the booking reference no : " << b.bookingRef << endl;
cout << "You will need the reference number to check the status of your booking" << endl;
afile.write(reinterpret_cast <const char *>(&b), sizeof(b));
afile.close(); // close append

Passing fstream to a function

I'm trying to pass an fstream to a function which then writes struct to the file. I'm aware that you have to pass the stream by reference, but nothing is being written to the file at runtime. Heres what I have so far:
struct Record
{
char name [16];
char phoneNum [16];
float balance;
};
void newRec (fstream &);
int main()
{
fstream ref;
ref.open("prog2.dat", ios::in | ios::out | ios::app | ios::binary);
if(!ref.fail() )
{
int choice = menu(ref);
system("CLS");
while(choice != 6)
{
choice = menu(ref);
system("CLS");
}
}
else
cout << "Error opening file. " << endl;
return 0;
}
void newRec (fstream& ref)
{
Record rec;
cout << "Enter customer name: ";
cin.ignore();
cin.getline(rec.name, sizeof(rec.name));
cout << "Enter customer phone number: ";
cin >> rec.phoneNum;
cout << "Enter beginning account balance: ";
cin >> rec.balance;
ref.write(reinterpret_cast<char*>(&rec), sizeof(rec));
}
rec being just a 3 member struct. Any ideas why this wouldn't work? I appreciate any help.
Note: I do have to use .write() as opposed to << as per my assignment
If you are using Visual Studio: maybe you are looking in the wrong directory, the file will be created in Projects\Project_Name\Project_Name when debugging, not in Projects\Project_Name\Debug.
Streams work just like cout and cin. You would probably be better off using
ref << rec.name << "," << rec.phoneNum << "," << rec.balance << endl;