【问题标题】:Problem while reading objects from file in c++在 C++ 中从文件中读取对象时出现问题
【发布时间】:2021-03-11 08:54:44
【问题描述】:

我正在做一个小型大学项目,我必须使用 OOP 概念在文件中添加、编辑和搜索记录。添加到文件中工作正常,但每当我尝试从文件中读取时,它都会以不可读的文本打印。 这是完整的代码和输出。 ma​​in.cpp

#include <iostream>
#include <cstdlib>
#include <fstream>
#define MIN 20
#define MAX 100
#include "student.h"

using namespace std;

void add_student();
void edit_student();
void search_student();
void addToFile(const Student&);
Student* fetchFromFile();
int getFileSize();
void updateFile(Student*);

// Student stud[MAX];
int main()
{
  int choice;
  system("cls");                                                  
  system("Color B0");
  while(1)
  {                                                                                            
    cout<<"\n\t\tWhat do you want to do?"<<endl;
    cout<<"\t\t----------------------"<<endl;                                                 
    cout<<"\t\t1-Add student"<<endl;                                                       
    cout<<"\t\t2-Edit student"<<endl;                                                         
    cout<<"\t\t3-Search student"<<endl;                                                   
    cout<<"\t\t4-Quit Program"<<endl;                                                              
    cout<<"\t\t----------------------"<<endl;            
    cout<<"Enter your choice: ";                           
    cin>>choice;                                           
    switch(choice)                                         
    {
      case 1:
        add_student(); //calling add_student function to add records.
        break;
      case 2:
        edit_student();
        break;
      case 3:
        search_student();
        break;
      case 4:
        return 0;
        break;
      default:
        cout<<"Invalid choice";
        break;
    }
}
  return 0;
}

int Student::id = getFileSize() - 1; //Initialize id equals to size of file

// setData function of class Student definition
void Student :: setData()
{
  // taking input from user
  cout<<"Enter student roll no in format(1XXX): ";
  cin>>roll;
  cout<<"Enter student name: ";
  cin>>name;
  cout<<"Enter stduent date of birth(dd/mm/yy): ";
  cin>>dob;
  cout<<"Enter stduent phone no: ";
  cin>>phone;
  cout<<"Enter student address: ";
  cin>>address;
  stdId = Student::id;
}

void Student :: showData()
{
  cout<<stdId<<"  ";
  cout<<roll<<"   ";
  cout<<name<<"     ";
  cout<<dob<<"\t";
  cout<<phone<<"   ";
  cout<<address<<"\n\n";
}

const int Student :: getRoll()
{
  return roll;
}

Student& Student::operator = (const Student& newObj)
{
  stdId = newObj.stdId;
  roll = newObj.roll;
  name = newObj.name;
  dob = newObj.dob;
  phone = newObj.phone;
  address = newObj.address;
  return *this;
}

void add_student()
{
  Student stud;
  Student::incrementId();
  stud.setData();
  addToFile(stud); //adding records to file
  system("CLS");
  cout<<endl;
  cout<<"--------------------------------------------------------------------------------"<<endl;
  cout<<"---------------------------Student updated record Table---------------------------------"<<endl;
  cout<<"--------------------------------------------------------------------------------"<<endl;
  cout<<"ID      "<<"Roll      "<<"Name      "<<"DOB      "<<"Phone no         "<<"Address\n\n";
  cout<<"--------------------------------------------------------------------------------"<<endl;

  Student* student = fetchFromFile(); //getting records from file in array of objects
  int length = getFileSize(); //getting length of array of objects

  for(int i=0; i<(length-1); i++)
  {
    student[i].showData(); //showing all the data
  }

  cout<<"--------------------------------------------------------------------------------"<<endl;
  cout<<"---------------------------------FINISH-----------------------------------------"<<endl;
  cout<<"--------------------------------------------------------------------------------"<<endl;
  cout<<"You want to add more?(Y/n):  ";
  char c;
  cin>>c;
  if(c=='y' || c=='Y')
  {
    add_student();
  }
  else{
    system("pause");
  }
}

void edit_student(){
  //Showing existing record first before editing
  cout<<endl;
  cout<<"--------------------------------------------------------------------------------"<<endl;
  cout<<"---------------------------Student Existing record Table---------------------------------"<<endl;
  cout<<"--------------------------------------------------------------------------------"<<endl;
  cout<<"ID   "<<"Roll   "<<"Name      "<<"DOB      "<<"Phone no         "<<"Address\n\n";
  cout<<"--------------------------------------------------------------------------------"<<endl;

  Student* student = fetchFromFile(); //fetching all records from file
  int length = getFileSize();

  for(int i=0; i<(length-1); i++)
  {
    student[i].showData();
  }
  int idnumber;
  cout<<"--------------------------------------------------------------------------------"<<endl;
  cout<<"Which ID number your want to edit: ";

  cin>>idnumber;            //Asking the user at which ID he wants to make a change.
  //checking for valid id number
  if(idnumber>length || idnumber<0)
  {
    cout<<"\nInvalid ID Number."<<endl;
  }
  //showing existing information about that specific record
  cout<<"\nExisted information about this record.\n\n";
  cout<<"--------------------------------------------------------------------------------"<<endl;
  cout<<"ID   "<<"Roll   "<<"Name      "<<"Father\tCell no.      "<<"DOB          "<<"Address\n\n";
  cout<<"--------------------------------------------------------------------------------"<<endl;
  student[idnumber].showData();
  cout<<"\n\nEnter new data for above shown record.\n\n";
  student[idnumber].setData();         //Inputting data for that specific record.
  updateFile(student);
  cout<<"\n\nRecord updated successfully."<<endl;
  cout<<endl;
  cout<<"--------------------------------------------------------------------------------"<<endl;
  cout<<"---------------------------Updated record Table---------------------------------"<<endl;
  cout<<"--------------------------------------------------------------------------------"<<endl;
  cout<<"ID   "<<"Roll   "<<"Name      "<<"DOB      "<<"Phone no         "<<"Address\n\n";
  cout<<"--------------------------------------------------------------------------------"<<endl;
  for(int i=0; i<(length-1); i++) //Showing updated record Table
  {
    student[i].showData();
  }
}

void search_student(){
  Student* student = fetchFromFile();
  int fileLenth = getFileSize() - 1;
  int searchkey;
  cout<<"Enter roll_no of student you want to search: ";
  cin>>searchkey;     //roll_no as the search key can be entered by user.
  for(int i=1; i<fileLenth; i++)
  {
    if(searchkey==student[i].getRoll()) //checking for roll no
    {
      student[i].showData();
    }
  }
  cout<<"--------------------------------------------------------------------------------"<<endl;
  cout<<"---------------------------------FINISH-----------------------------------------"<<endl;
  cout<<"--------------------------------------------------------------------------------"<<endl;
  system("pause");
}

//FILE HANDLING

void addToFile(const Student& obj)
{
  ofstream fout;
  fout.open("records.txt", std::ofstream::app | std::ofstream::binary);
  fout.write((char*)&obj, sizeof(obj));
  cout<<"Added to file successfully!"<<endl;
  fout.close();
}

Student* fetchFromFile()
{
  int i=0;
  Student obj;
  Student* returnObj = new Student[MAX];
  ifstream fin;
  fin.open("records.txt", std::ifstream::binary);
  while(!fin.eof())
  {
    fin.read((char*)&obj, sizeof(obj));
    returnObj[i] = obj;
    i++;
  }
  fin.close();
  delete[] returnObj;
  return returnObj;
}

int getFileSize()
{
  int i=0;
  Student obj;
  ifstream fin;
  fin.open("records.txt", std::ifstream::binary);
  while(!fin.eof())
  {
    fin.read((char*)&obj, sizeof(obj));
    i++;
  }
  fin.close();
  return i;
}

void updateFile(Student* student)
{
  ofstream fout;
  fout.open("records.txt", std::ofstream::binary);
  fout.write((char*)&student, sizeof(student));
  fout.close();
}

student.h 头文件

// A student class that hold students attributes like id, name, address and class
// Object of this class will be craeted to store student details
class Student
{
  int stdId;
  int roll;
  std::string name;
  std::string dob;
  std::string phone;
  std::string address;
public:
  static int id; //we will increase 'id' whenever student is added to the record
  //Member functions declaration
  void setData(); //this function will take input from user and set the data to attributes of class
  void showData(); //This function will give student data to user when called
  static void incrementId()
  {
    id++;
  }
  const int getRoll();
  Student& operator = (const Student&);
};

示例输出 1

When I add a student object to file

示例输出 2

Reading all records from file

问题1:显示id的垃圾值。

示例输出 3

Adding another object to file

示例输出 4

Reading all objects from file

示例输出 5

Now went back and chose to edit record

问题 2:查看记录如何以不可读的形式打印。

为什么会这样。现在,如果我关闭程序并再次运行它,它仍然以不可读的文本显示。 希望你能得到我的问题。我想对此有详细的解释。另外,如果我犯了其他杂项错误,请告诉我。 谢谢!

【问题讨论】:

  • 有一个很好的副本涵盖了我现在无法找到的这个问题。最简单的归结为string 是指向char 数组和整数的指针。将string 写入文件并写入指针,而不是指针指向的内容。该指针对您无用,因为当您读回文件时,string 不再在内存中并且指针指向其他人的内存,或者您有两个 string 副本并且当一个副本用完时范围它带有char 数组,而另一个副本现在是定时炸弹。
  • 你应该做的是试验一下,看看问题出在哪里(即你的字符串成员),并使用这些信息将你的代码大大减少到minimal reproducible example。将您的代码示例的复杂度与debug read/write string to binary fileHow to write std::string to file? 中的复杂度进行比较
  • 这能回答你的问题吗? How to write std::string to file?
  • You need to serialize the data。最简单的做法是放弃readwrite。您的大部分数据是字符串数据,因此为Student 编写&lt;&lt;&gt;&gt; 重载并将所有内容存储为文本。在每个成员之间添加逗号或其他内容,以便您可以处理名称和其他包含空格或其他分隔符的成员,这些分隔符会导致读取数据时出错。
  • githubgitlab 上寻找现有开源 C++ 项目源代码的灵感,也可以在 RefPerSysSWIGClang .阅读编译器(例如GCC ...)和调试器(例如GDB...)的文档

标签: c++ oop file-handling


【解决方案1】:

Student 太复杂,无法使用readwrite 等未格式化的 IO 读取。用技术术语来说,它不是Trivially Copyable,因为它包含std::strings,并且string 不是 Trivially Copyable。

最简单的做法是放弃readwrite。您的大部分数据都是字符串数据,因此请为Student 编写&lt;&lt;&gt;&gt; 重载并将所有内容存储为文本。

    friend std::ostream & operator<<(std::ostream & out, 
                                     const Student & stu) 
    { 
        out << stu.stdId << ',' <<stu.roll << ','<< stu.name... <<'\n'; 
        return out;
    } 

读回数据有点棘手

    friend std::istream & operator>>(std::istream & in, 
                                     Student & stu) 
    { 
        std::string line;
        if (std::getline(in, line))
        {
            std::stringstream strm(line);
            if (!(strm >> stu.stdId >> comma >> stu.roll >> comma) ||
                !std::getline(strm, stu.name, ',') ||
                !std::getline(strm, stu.dob, ',') ||
                ...))
            { // If any read failed, mark the stream as failed so the caller knows.
                out.setstate(std::ios_base::failbit);
            }
        }
        return out;
    } 

【讨论】:

    【解决方案2】:

    首先,一个小技巧,为类添加一个空的构造函数来初始化所有成员、整数{0}和字符串{},这将消除一些不需要的垃圾。

     Student::Student() :stdId(0), roll(0), name{}, dob{}, phone{}, address{} {;}
    

    您的主要问题来自函数 fetchfomrfile(),您在该函数中删除了获取的 Student 数组,因此调用者收到了一个未定义的数据数组:

    Student* fetchFromFile()
    {
      int i=0;
      Student obj;
      Student* returnObj = new Student[MAX];
    
      //reading records from file
      fin.close();
      delete[] returnObj;// <<<< youe deleted
      return returnObj; // and return it as undefined
    }
    

    既然你可以计算文件的大小,我建议在调用函数中:

    void search_student(){
     // Student* student = fetchFromFile();
        Student *student = new Stduent [getFileSize()];
         fecchFromFile(student); // use this array in fetch_file 
      // other things
     }
    

    将 fetchFromFile 重写为:

    viod fetchFromFile(Stduent *ss)
    {
      // read file data to array ss[i];
    }
    

    【讨论】:

      猜你喜欢
      • 2021-12-20
      • 1970-01-01
      • 2018-08-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-11
      • 1970-01-01
      相关资源
      最近更新 更多