使用类在 c++ 中输入字符串无法正常工作
Inputing strings in c++ using class is not working properly
#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;
}
我无法识别代码中的错误...我已经交叉检查了代码...没有语法错误和编译器错误,但输出不正确...它没有从用户那里获取正确的输入。
-
正如Bo Persson所说,你的程序没有为名称和名称提供任何内存。您可以在类中声明像
name
这样的指针,但它们不指向任何内容。例如,您可以将名称声明为char name[100];
,并希望 100 就足够了(并且在生产代码中,添加检查以确保不超过它)。但是在C++有字符串类可以免除您的许多后顾之忧。特别是,它使任意长字符串的输入变得容易。如果字符串中不能包含空格,则一个简单的
string s; cin >> s;
从输入中读取字符串中的"单词"。如果它可以有空格,你需要一些方法来告诉它在哪里开始和结束。数据库,Excel等经常使用引号将字符串括起来。如果它不能包含换行符,则确保它位于自己的行上就足够了,并且无需解析。这就是我们在这里要做的。 -
您面临的第二个问题是技术上称为"序列化"。您希望将类存储("持久化")在磁盘上,然后(也许是很久以后)将其重新读取到新实例中。内森·奥利弗(Nathan Oliver)向您指出了一个讨论序列化的资源。对于像
employee
这样具有有限数量的简单数据成员的简单类,我们可以简单地滚动我们自己的临时序列化:我们使用operator<<()
将所有内容写入磁盘,并使用operator>>()
读回所有内容。 -
要考虑的主要事情是字符串可能有空格,因此我们将它们放在自己的行上。
-
另外,为了在从文件中读取时更加健壮,我们用开始标记开始每个员工(
header
下面的代码)。这样,读取员工将从文件中的任何位置工作。此外,如果后来的员工应该有更多的字段,我们仍然可以读取我们的基本员工数据,并在我们读取磁盘上员工序列中的下一个员工之前跳过其他字段。 -
当流在其作用域结束时被销毁时,它们会自动关闭;我们为此使用块作用域(检查代码中的附加
{}
)。 -
对于更高的薪水来说,普通
float
不够精确(它只有大约 7 个十进制数字,因此对于工资> 167772.16(如果我能相信维基百科的话),无论以何种货币计算,便士开始从悬崖上掉下来)。我使用long double
并确保在将其转换为文本时不会失去精度。 -
你没有比较真实,但我这样做是为了检查我是否正确地阅读了员工。那里必须小心。我通过比较半便士来摆脱血腥的细节,这应该适合金钱。
这是整个程序。(与我以前的版本相比,我简化了(反)序列化,特别是我删除了无用的标签。
明智的做法是在每次读/写后执行错误检查,以确保它成功;记住,流隐蔽到布尔值,所以一个简单的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;
}
}
}
示例会话:
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
除了评论中所说的gets
危险之外,您还会立即遇到缓冲区溢出,因为没有为name
分配任何空间。仅仅有一个指针是不够的。
除此之外,混合cin >>
和getline(cin,...)
已知会跳过输入,因为这两个函数处理行尾的方式不同。
然后我们遇到了为 employee
类型执行二进制 I/O 的问题。通常,不能对非平凡的类类型执行此操作。具体而言,std::string designation
成员在内部保存指向某些数据的指针。该指针将无法在传输到磁盘和返回后继续存在。
- QSqlquery prepare()和bindvalue()不工作
- 导入库可以跨dll版本工作吗
- 以螺旋方式打印矩阵的程序.(工作不好)
- 对象指针在c++中是如何工作的
- 为什么在Windows上的VS 2019和Clang 9中"size_t"在没有标题的情况下工作
- VSOMEIP-2个设备之间的通信(TCP/UDP)不工作
- 为字符串中每 N 个字符插入空格的函数没有按照我认为的方式工作?
- C++为线程工作动态地分割例程
- 为什么我的 std::ref 无法按预期工作?
- 布尔比较运算符是如何在C++中工作的
- SampleConsensusPrerejective(ext.RANSAC)是如何真正工作的
- 不确定要在我的main中放入什么才能使我的代码正常工作
- 为什么std::condition_variable notify_all的工作速度比notify_one快(对于随机请
- <<操作员在下面的行中工作
- 有人能解释一下为什么下界是这样工作的吗C++的
- ExtractIconEx:可以工作,但偶尔会崩溃
- C++中的memset函数工作不正常
- 当我在第一个循环中使用"auto"时,它工作正常,但是使用"int"它会给出错误,为什么?
- 为什么STD ::计数将常数传递给Lambda,而不是在弦上工作时而不是字符
- C++程序已停止工作-求解常微分方程