Passing fstream to a function - c++

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;

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.

File program won't stop displaying garbage values

I am trying to insert object in file and then read the object to display the student data but when It goes to display program just goes in infinite loop and starts displaying 0 which I have initialized in constructor.I am simply not getting what is happening. I am using visual studio 17 just in case anyones wondering. I even tried to create a new file named Student.txt in same directory as the program but it won't work. Can somone explain me what I am doing wrong?
#include <iostream>
#include <fstream>
#include <cstring>
using namespace std;
//class to handle individual record
class Student
{
public:
char name[20];
int year;
char division;
char address[50];
int rollno;
Student()
{
strcpy_s(name," ");
strcpy_s(address, " ");
rollno = 0;
year = 0;
division = 0;
}
};
class operations
{
public:
void insertdata();
void printg();
};
void operations::insertdata()
{
int n;
cout << "\nEnter how many student data you want to insert:";
cin >> n;
fstream fin;
Student obj;
fin.open("Student.txt", ios::in | ios::out | ios::binary| ios::trunc);
if (!fin)
{
cout<<"\nFILE NOT Opened!";
}
for (int v = 0; v < n; v++)
{
cout << "\nEnter Roll no:";
cin >> obj.rollno;
cout << "\nEnter Name:";
cin.ignore();
cin >> obj.name;
cout << "\nEnter year:";
cin >> obj.year;
cout << "\nEnter division:";
cin >> obj.division;
cout << "\nEnter Address:";
cin.ignore();
cin >> obj.address;
fin.seekp(0, ios::end);
fin.write((char*)&obj, sizeof(obj));
}
fin.close();
}
void operations::printg()
{
Student obj;
fstream fin("Student.txt", ios::in | ios::out | ios::binary);
fin.seekg(0, ios::beg);
fin.read((char*)&obj, sizeof(obj));
if (!fin)
{
cout << "\n FIle doenst exist";
}
while (!fin.eof())
{
cout << "\n" << obj.name;
cout << "\n" << obj.year;
cout << "\n" << obj.division;
}
fin.close();
}
int main() {
operations obj;
obj.insertdata();
obj.printg();
system("pause");
return 0;
}
A few wrong things:
Writing objects like fin.write((char*)&obj, sizeof(obj)); is a bad idea. A compiler may decide to have different padding between members at any moment for your Student objects, so your file format is like a quantum particle: you don't really know how the file was laid out.
strcpy_s takes 3 parameters, not 2. Anyway, do not use them, they are not really portable (even if they are in the C standard).
Your paths are wrong, so the file will not open (as Sam explains in the comment).
Even if you succeeded in opening a file, in operations::printg() you are not reading the file, so you will not get any data.
Why do you have an operations class? I guess it is intended to be expanded in the future, but seems weird. If you do not intend to have state, use a namespace instead.

How to read/write string type member of a struct using binary file in/out in c++?

I have 2 c++ code: one is for write data into a binary file, another is for read that file.
write.cpp code is as below:
#include <iostream>
#include <fstream>
using namespace std;
const int NAME_SIZE = 51;
struct Data
{
char name[NAME_SIZE];
int age;
};
int main()
{
Data person;
char again;
fstream people("people.db", ios::out | ios::binary);
do
{
cout << "Enter the following data about a "<< "person:\n";
cout << "Name: ";
cin.getline(person.name, NAME_SIZE);
cout << "Age: ";
cin >> person.age;
cin.ignore();
people.write(reinterpret_cast<char *>(&person),sizeof(person));
cout << "Do you want to enter another record? ";
cin >> again;
cin.ignore();
} while (again == 'Y' || again == 'y');
people.close();
return 0;
}
read.cpp code is as below:
#include <iostream>
#include <fstream>
using namespace std;
const int NAME_SIZE = 51;
struct Data
{
char name[NAME_SIZE];
int age;
};
int main()
{
Data person;
char again;
fstream people;
people.open("people.db", ios::in | ios::binary);
if (!people)
{
cout << "Error opening file. Program aborting.\n";
return 0;
}
cout << "Here are the people in the file:\n\n";
people.read(reinterpret_cast<char *>(&person),sizeof(person));
while (!people.eof())
{
cout << "Name: ";
cout << person.name << endl;
cout << "Age: ";
cout << person.age << endl;
cout << "\nPress the Enter key to see the next record.\n";
cin.get(again);
people.read(reinterpret_cast<char *>(&person),sizeof(person));
}
cout << "That's all the data in the file!\n";
people.close();
return 0;
}
Above mentioned codes work fine. The problem arises when I use string type members in the structure:
new write.cpp:
#include <iostream>
#include <fstream>
using namespace std;
struct Data
{
string name;
int age;
};
int main()
{
Data person;
char again;
fstream people("people.db", ios::out | ios::binary);
do
{
cout << "Enter the following data about a "<< "person:\n";
cout << "Name: ";
cin>>person.name;
cout << "Age: ";
cin >> person.age;
cin.ignore();
people.write(reinterpret_cast<char *>(&person),sizeof(person));
cout << "Do you want to enter another record? ";
cin >> again;
cin.ignore();
} while (again == 'Y' || again == 'y');
people.close();
return 0;
}
new read.cpp:
#include <iostream>
#include <fstream>
using namespace std;
struct Data
{
string name;
int age;
};
int main()
{
Data person;
char again;
fstream people;
people.open("people.db", ios::in | ios::binary);
if (!people)
{
cout << "Error opening file. Program aborting.\n";
return 0;
}
cout << "Here are the people in the file:\n\n";
people.read(reinterpret_cast<char *>(&person),sizeof(person));
while (!people.eof())
{
cout << "Name: ";
cout << person.name << endl;
cout << "Age: ";
cout << person.age << endl;
cout << "\nPress the Enter key to see the next record.\n";
cin.get(again);
people.read(reinterpret_cast<char *>(&person),sizeof(person));
}
cout << "That's all the data in the file!\n";
people.close();
return 0;
}
Now when I run read.cpp the program can't read string and the program crashes. I must use string as a member of the structure. How to solve this problem?
The only way that comes to mind is to write the following data separately:
Length of the string.
The array of characters of the string.
The age.
and read them separately.
Create functions to write/read an instance of Data such that they are aware of each other's implementation strategy.
std::ostream& write(std::ostream& out, Data const& data)
{
size_t len = data.name.size();
out.write(reinterpret_cast<char const*>(&len), sizeof(len));
out.write(data.name.c_str(), len);
out.write(reinterpret_cast<char const*>(&data.age));
return out;
}
std::istream& read(std::istream& in, Data& data)
{
size_t len;
in.read(reinterpret_cast<char*>(&len), sizeof(len));
char* name = new char[len+1];
in.read(name, len);
name[len] = '\0';
data.name = name;
delete [] name;
in.read(reinterpret_cast<char*>(&data.age));
return in;
}
and use them similarly to your first approach.
Instead of using
people.write(reinterpret_cast<char *>(&person),sizeof(person));
use
write(people, person);
Instead of using
people.read(reinterpret_cast<char *>(&person),sizeof(person));
use
read(people, person);
One problem is that sizeof(person.Name) does not give what you think it does. It always gives the same size (28 bytes in my case) not matter what characters you assign to your person.Name string. This is because of std::string contains at least:
a pointer to the actual string
other data structure to hold the available size and the size used
Therefore, you cannot call people.write(reinterpret_cast<char *>(&person),sizeof(person));. The content of your string is not located at &person (its located wherever the pointer in std::string is pointing to)
So, what happens when you do cout << person.name << endl; after reading it from your file? You've actually read the address (not the content) where person.name's string pointer was pointing to, when you wrote person to people.db. This is of course not a valid memory location, after reading it from your file, again.
The following code snippet could be helpful in your case. Instead of writing the length of the string, it is possible to use a delimiter and a pre-defined string length.
constexpr char delimiter = '\0';
constexpr uint32_t maxStringSize = 1024;
struct Data
{
string name;
int age;
};
When writing the file, place a delimiter after the string.
Let's say we have a Data structure {"John", 42} then we would write as follows:
std::ofstream outStream(filename, std::ios::binary);
outStream << structure.name << delimiter << structure.age;
outStream.close();
Reading the file is not the mirror of the write (unfortunately).
We'll use the std::ifstream::getline to read the string without knowing the size of it. (Error checking omitted)
std::ifstream istrm(filename, std::ios::binary);
Data dataRead;
// string input - use a buffer and look for the next delimiter
char* buf = new char[maxStringSize];
istrm.getline(buf, maxStringSize, delimiter);
dataRead.name = std::string(buf);
// the number input
istrm >> dataRead.age;
For inspiration how to read/write a vector of this struct you can check my repository.

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.

Binary files with Struct, classes, fstream error

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;