-
As Bo Persson said, your program does not provide any memory for name and designation. You declare pointers like
name
in your class alright, but they do not point to anything. You could just declare e.g. name aschar name[100];
and hope that 100 is enough (and, in production code, add checks to ensure it isn’t exceeded).But in C++ there is the string class which frees you of many worries. In particular, it makes input of arbitrarily long strings easy. If the string can’t have whitespace in it, a simple
string s; cin >> s;
reads a “word” from the input into the string. If it can have whitespace, you need some way to tell where it starts and ends. Databases, Excel etc. often use quotes to surround a string. If it cannot contain newlines, ensuring that it is on a line of its own is sufficient and obviates the need for parsing. That’s what we’ll do here. -
The second issue you faced is what’s technically called “serialization”. You want to store (“persist”) your class on disk and later (much later, perhaps) re-read it into a new instance. Nathan Oliver pointed you to a resource which discusses serialization. For a simple class like
employee
with a limited number of simple data members we can simply roll our own ad-hoc serialization: We write everything to disk withoperator<<()
, and read everything back withoperator>>()
. -
The main thing to consider is that strings may have whitespace, so that we’ll put them on a line of their own.
-
An addition is that in order to be more robust when reading from a file we start each employee with a start marker (
header
in the code below). This way reading an employee will work from any position in the file. Also, if later employees should have more fields we can still read our basic employee data and just skip the additional fields before we read the next employee in a sequence of employees on disk. -
streams are closed automatically when they are destroyed at the end of their scope; we use block scope for that (check the additional
{}
in the code). -
A plain
float
is not precise enough for higher salaries (it only has about 7 decimal digits, so that for salaries > 167772.16 (if I can believe Wikipedia) in whatever currency the pennies start to fall off the cliff). I uselong double
and make sure to not lose precision when converting it to text. -
You didn’t compare reals but I did in order to check whether I read the employee back correctly. Care must be taken there. I wiggle out of the gory details by comparing half pennies which should be appropriate for money.
Here is the whole program. (Compared with my previous version I have simplified the (de)serialization, in particular I have eliminated the useless tags.)
It would be wise to perform error checking after each read/write in order to make sure it succeeded; remember, a stream coverts to bool, so a simple if(!os) { cerr << "oops" << endl; /* exit? */}
is enough; but I didn’t want to distract from the actual program too much.
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include <cfloat> // for LDBL_DIG in ostream precision.
#include <cstdlib> // for exit()
using namespace std;
/** A simple class holding employee information */
class employee
{
public:
/** This header starts each
"serialization" of an employee on a line
of its own. */
static constexpr const char *header = "--- Employee ---";
// use C++ std::string
string name;
int age;
// use C++ std::string
string designation;
// Be as precise as possible,
// for later uses like salary increases
// by 4.5% or so :-)
// The salary is in units like USD or EUR.
// The fraction part is pennies, and fractions of them.
long double salary;
public:
void readdata(istream &is);
void writedata(ostream &os);
void display();
bool operator==(employee &rhs)
{ return
name == rhs.name
&& age == rhs.age
&& designation == rhs.designation
// Do not compare floats directly.
// We compare pannies, leaving slack for rounding.
// If two salaries round to the same penny value,
// i.e. 0.01, they are equal for us.
// (This may not be correct, for an accounting app,
// but will do here.)
&& salary - rhs.salary < 0.005
&& rhs.salary - salary < 0.005;
}
};
/** Write a header and then
each data member in declaration order, converted to text,
to the given stream. The header is used to find the start
of the next employee; that way we can have comments or other
information in the file between employees.
The conversion is left to operator<<.
Each member is written to a line of its own, so that we
can store whitespace in them if applicable.
The result is intended to be readable by @readdata().
*/
void employee::writedata(ostream &os)
{
os.precision(LDBL_DIG); // do not round the long double when printing
// make sure to start on a new line....
os << endl
// ... write the header on a single line ...
<< header << endl
// ... and then the data members.
<< name << endl
<< age << endl
<< designation << endl
<< salary << endl;
}
/**
Read an amployee back which was written with @writedata().
We first skip lines until we hit a header line,
because that's how an employee record starts.
Then we read normal data mambers with operator>>.
(Strictly spoken, they do not have to be on lines
of thier own.)
Strings are always on a line of their own,
so we remove a newline first.
*/
void employee::readdata(istream &is)
{
string buf;
while(getline(is, buf)) // stream converts to bool; true is "ok"
{
if( buf == header) break; // wait for start of employee
}
if( buf != header )
{
cerr << "Error: Didn't find employee" << endl;
return;
}
getline(is, name); // eats newline, too
is >> age; // does not eat newline:
// therefore skip all up to and including the next newline
is.ignore(1000, '\n');
// line on its own, skips newline
getline(is, designation);
is >> salary;
}
int main()
{
const char *const fname = "emp.txt";
employee empa;
empa.name = "Peter A. Schneider";
empa.age = 42;
empa.designation = "Bicycle Repair Man";
empa.salary = 12345.67;
employee empb;
empb.name = "Peter B. Schneider";
empb.age = 43;
empb.designation = "Bicycle Repair Woman";
empb.salary = 123456.78;
{
ofstream os(fname);
if(!os)
{
cerr << "Couldn't open "
<< fname << " for writing, aborting" << endl;
exit(1);
}
empa.writedata(os);
cout << "Employee dump:" << endl;
empa.writedata(cout);
// insert a few funny non-employee lines
os << endl << "djasdlköjsdj" << endl << endl;
empb.writedata(os);
cout << "Employee dump:" << endl;
empb.writedata(cout);
}
// show the file contents
{
ifstream is(fname);
if(!is)
{
cerr << "Couldn't open "
<< fname << " for reading, aborting" << endl;
exit(1);
}
string line;
cout << "-------------- File: -------------" << endl;
while(getline(is, line)) cout << line << endl;
cout << "---------------End file ----------" << endl;
}
/////////////////////////////////////////////////////////
{
ifstream is(fname); // read from the file "emp.txt" just written
if(!is)
{
cerr << "Couldn't open "
<< fname << " for reading, aborting" << endl;
exit(1);
}
{
employee emp2; // new employee, sure to be empty
cout << endl << "Re-reading an employee..." << endl;
emp2.readdata(is);
cout << endl << "Re-read employee dump:" << endl;
emp2.writedata(cout);
cout << "Original and written/read employee are "
<< (empa == emp2 ? "" : "NOT ") << "equal" << endl;
}
{
employee emp2; // new employee, sure to be empty
// now read next employee from same stream.
// readdata() should skip garbage until the header is found.
cout << endl << "Re-reading an employee..." << endl;
emp2.readdata(is);
cout << endl << "Re-read employee dump:" << endl;
emp2.writedata(cout);
cout << "Original and written/read employee are "
<< (empb == emp2 ? "" : "NOT ") << "equal" << endl;
}
}
}
Sample session:
Employee dump:
--- Employee ---
Peter A. Schneider
42
Bicycle Repair Man
12345.6700000000001
Employee dump:
--- Employee ---
Peter B. Schneider
43
Bicycle Repair Woman
123456.779999999999
-------------- File: -------------
--- Employee ---
Peter A. Schneider
42
Bicycle Repair Man
12345.6700000000001
djasdlköjsdj
--- Employee ---
Peter B. Schneider
43
Bicycle Repair Woman
123456.779999999999
---------------End file ----------
Re-reading an employee...
Re-read employee dump:
--- Employee ---
Peter A. Schneider
42
Bicycle Repair Man
12345.6700000000001
Original and written/read employee are equal
Re-reading an employee...
Re-read employee dump:
--- Employee ---
Peter B. Schneider
43
Bicycle Repair Woman
123456.779999999999
Original and written/read employee are equal
solved Inputing strings in c++ using class is not working properly