#include<fstream>
#include<iostream>
using namespace std;
class employee
{
char *name;
int age;
string designation; // string data type is used
float salary;
public:
void getdata();
void display();
};
void employee::getdata() // for taking the input
{
cout<<"\nENTER THE NAME OF THE EMPLOYEE: ";
gets(name); /*name is a char pointer */
cout<<"\nENTER THE AGE OF THE EMPLOYEE: ";
cin>>age;
cout<<"\nENTER THE DESIGNATION OF THE EMPLOYEE: ";
getline(cin,designation); /*designation is string data type*/
cout<<"\nENTER THE SALARY OF THE EMPLOYEE: ";
cin>>salary;
}
void employee::display()//for displaying the inputed data
{
cout<<"\nTHE NAME OF THE EMPLOYEE: ";
puts(name);
cout<<"\nENTER THE AGE OF THE EMPLOYEE: ";
cout<<age;
cout<<"\nENTER THE DESIGNATION OF THE EMPLOYEE: ";
cout<<designation;
cout<<"ENTER THE SALARY OF THE EMPLOYEE: ";
cout<<salary;
}
int main()
{
ofstream fout;
char ch;
fout.open("employee.txt",ios::out|ios::binary);/*use of open function in file handing*/
employee e;
for(int i=0;i<3;i++)
{
e.getdata();
fout.write((char*)&e,sizeof(e));//write() is used
}
fout.close();
ifstream fin;
fin.open("employee.txt",ios::in|ios::binary);
int j;
cout<<"\n Enter the employee no. to be read ";
cin>>j;
fin.seekg((j-1)*sizeof(e));
fin.read((char*)&e,sizeof(e));
cout<<"\n Details of employee no. of object is = ";
e.display();
return 0;
}
我无法识别我的代码中的错误...我已经交叉检查了代码...没有语法错误,没有编译器错误,但输出不正确...它没有采取正确的来自用户的输入。
答案 0 :(得分:1)
除了评论中关于gets
危险的内容之外,您还会通过不为name
分配任何空间来立即遇到缓冲区溢出。仅仅使用指针是不够的。
除此之外,已知混合cin >>
和getline(cin,...)
会跳过输入,因为the two functions handle end-of-line differently。
然后我们遇到了为employee
类型执行二进制I / O的问题。通常,对于非平凡的类类型,您不能这样做。具体来说,std::string designation
成员在内部持有指向某些数据的指针。该指针不会存在the transfer to disk and back。
答案 1 :(得分:1)
正如Bo Persson所说,你的程序没有为名称和名称提供任何记忆。你在类中声明指针就像name
一样,但它们并没有指向任何东西。你可以宣布例如命名为char name[100];
并希望100足够(并且在生产代码中添加检查以确保不超过)。
但是在C ++中有一个字符串类可以让你摆脱许多担忧。特别是,它使得任意长串的输入变得容易。如果字符串中不能包含空格,则简单的string s; cin >> s;
会从输入中读取字符串中的“单词”。如果它可以有空格,你需要一些方法来判断它的开始和结束位置。数据库,Excel等通常使用引号来包围字符串。如果它不能包含换行符,那么确保它在一行上就足够了,并且不需要解析。这就是我们在这里要做的事情。
您遇到的第二个问题是技术上称为“序列化”的问题。您希望将您的类存储(“持久化”)在磁盘上,稍后(可能很晚以后)将其重新读入新实例。 Nathan Oliver向您介绍了一个讨论序列化的资源。对于像employee
这样简单的类,只有有限数量的简单数据成员,我们可以简单地推广我们自己的特殊序列化:我们用operator<<()
将所有内容写入磁盘,并使用{{1}读取所有内容}。
要考虑的主要事情是字符串可能有空格,以便我们将它们放在自己的行上。
另外一点是,为了在从文件中读取时更加健壮,我们使用开始标记(下面的代码中的operator>>()
)启动每个员工。这样,阅读员工将从文件中的任何位置开始工作。此外,如果以后的员工应该有更多的字段,我们仍然可以阅读我们的基本员工数据,并在我们阅读磁盘上一系列员工中的下一位员工之前跳过其他字段。
当流在其范围结束时被销毁时,它们会自动关闭;我们使用块范围(检查代码中的附加header
)。
一个简单的{}
长双精度数字,确保在将其转换为文本时不会失去精确度。
您没有比较实际,但我这样做是为了检查我是否正确地阅读了员工。必须小心。通过比较应该适合赚钱的半便士,我摆脱了血腥的细节。
这是整个计划。 (与我之前的版本相比,我简化了(de)序列化,特别是我已经删除了无用的标签。)
在每次读/写之后执行错误检查是明智的,以确保它成功;记住,一条小溪转向bool,所以一个简单的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 use
就足够了;但是我不想过多地分散实际节目的注意力。
if(!os) { cerr << "oops" << endl; /* exit? */}
示例会话:
#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;
}
}
}