使用类在c ++中输入字符串无法正常工作

时间:2016-02-29 16:44:22

标签: c++

#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;
}

我无法识别我的代码中的错误...我已经交叉检查了代码...没有语法错误,没有编译器错误,但输出不正确...它没有采取正确的来自用户的输入。

2 个答案:

答案 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;
        }
    }
}