C++ 如何到达文件末尾

C++ 如何到达文件末尾,c++,file,struct,C++,File,Struct,我有一个函数,它从文件中获取一些信息并将其放入一个结构中。我设置了一个循环(分别为和设置和设置和设置,条件是循环一直持续到文件末尾。 但它不起作用 void FileToStructProfessors() { int n; fstream CurrentFile("professor.txt"); if (!CurrentFile) { cout <<"can't open file"; } else

我有一个函数,它从文件中获取一些信息并将其放入一个结构中。我设置了一个循环(分别为设置设置设置,条件是循环一直持续到文件末尾。 但它不起作用

void FileToStructProfessors()
{
     int n;
     fstream CurrentFile("professor.txt");
     if (!CurrentFile)
     {
         cout <<"can't open file";
     }
     else
     {
         CurrentFile.seekp(5);
         /*for(n=0;CurrentFile.eof();n++)
         {
             CurrentFile>>P1[n].FirstName;
             CurrentFile>>P1[n].LastName;
             CurrentFile>>P1[n].PID;
             CurrentFile>>P1[n].Major;
             CurrentFile>>P1[n].Level;
             CurrentFile.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
             cout<<P1[0].FirstName<<endl;

         }*/
         n=0;
         while(!CurrentFile.eof())
         {
             CurrentFile>>P1[n].FirstName;
             CurrentFile>>P1[n].LastName;
             CurrentFile>>P1[n].PID;
             CurrentFile>>P1[n].Major;
             CurrentFile>>P1[n].Level;
             CurrentFile>>P1[n].Date;
             CurrentFile.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
             cout<<P1[3].FirstName<<endl; 
             n++;               
         }
     }
     CurrentFile.close();
}
针对教授结构

struct Professors
{
       string FirstName,LastName;
       long long int PID;
       string Major,Level,Date;
}P1[100];

您的问题是,您正在尝试读取行,但是
CurrentFile>
忽略了所有的空白,包括空格。所以当你读到第一位教授时:

med
hamidi
1001
civil
associate professor
91/02/15
您将获得:

FirstName = med
LastName = hamidi
PID = 1001
Major = civil
Level = associate
Date = professor
“associate”和“professor”之间有一个空格,因此
CurrentFile>>P1[n]。Level
只选择“associate”,因为
CurrentFile>
看到了空格并停止。然后,你的下一个
CurrentFile>>
选择“professor”,而不是你想读的日期。对整个文件进行复合,您将得到大量没有意义的输入

您可以使用
getline(当前文件,*要读取的字符串*)
解决此问题,如以下示例所示。请注意,这不是生产质量代码,只是一个简单的玩具示例,演示了
getline
,旨在帮助您推进项目

void FileToStructProfessors()
{
int n;
fstream当前文件(“professor.txt”);
if(CurrentFile.fail())
{
cout>P1[n].PID;
getline(当前文件,P1[n].Major);
getline(当前文件,P1[n].Level);
getline(当前文件,P1[n].日期);
这是读过去的空白行。
//分隔您的条目。
getline(当前文件,s);
//印刷品。
n++;
}
}
CurrentFile.close();
}
顺便说一下,我删除了:

  • CurrentFile.seekp(5)
  • CurrentFile.ignore(std::numeric_limits::max(),'\n')
  • 注意

    struct Professors
    {
           string FirstName,LastName;
           long long int PID;
           string Major,Level,Date;
    }P1[100];
    

    我的示例不安全/不正确,因为那些
    getline
    调用或
    CurrentFile>
    语句中的任何一个都可能遇到错误,并且不会检查它们是否有错误。我写这篇文章是为了相对简单地解释为什么代码出现问题。对于安全/正确的代码,请查看。

    执行输入的条件不应以获取文件结尾为前提。仅在输入失败之前执行输入是执行提取的常规条件。否则会导致不必要的行为

    因此,您应该检查流是否没有设置
    std::ios_base::failbit
    std::ios_base::badbit
    。检查这两个位是必要的,因为
    failbit
    表示流无法成功提取的解析错误,
    badbit
    表示外部设备有问题(可能是不可恢复的错误)。两者都会导致提取失败。但这一切都需要在尝试输入后完成

    以下是使用您的代码表示的上述任务:

    请注意,我还通过使用
    std::getline()
    更改了最后两个提取。这是因为文件中的相应数据包含格式化运算符用作分隔符的空格。日期使用斜杠,当使用格式化输入时,斜杠也会被分隔。然后,
    级别
    日期
    数据成员必须是
    std::string
    s,并且需要使用无格式输入

    如果任何一个提取失败,流将设置适当的位。然后,流将使用
    运算符bool()
    (或
    运算符void*()
    pre-C++11隐式转换为布尔值,随后将升级为布尔值)。布尔运算将使用
    检查流状态!此->fail()
    (同时检查
    badbit
    failbit
    ),如果函数返回true,则整个
    while
    循环表达式为false,并且不会执行剩余的提取(因为短路计算)

    如果流仍处于良好状态(
    !this->fail()
    返回
    true
    ),则将执行循环体。如果流在执行提取时遇到EOF字符,将设置
    eofbit
    ,但循环不会停止,直到稍后尝试的输入操作未能成功提取。这是理想的行为。

    改进您的程序: 那是你问题的症结所在。您不了解流输入是如何执行的。我不知道是谁教你
    ,而(!file.eof())
    ,但这几乎总是错误的

    你的程序很好,但并不完美。如果你想知道如何改进你的程序,请继续阅读,否则我希望我的其他答案会有所帮助

    您没有向我们显示相关的代码,但我假设
    P1
    是一个默认大小的静态数组(该大小为4)。由于索引
    n
    随着文件内容的增加而增加,因此您的代码具有潜在的危险性。如果要提取到数组中的数据超过4个“组”,则程序将调用未定义的行为

    为了避免这个潜在问题,可以使用动态容器,如
    std::vector
    。它将分配一个适当大小的内部数组,并根据需要动态调整大小。可以按如下方式指定构造时的尺寸:

    上面的代码出现在您的while循环中(我相信),试图丢弃空格和换行符,以便可以提取的下一个字符是下一位教授的名字。出于上述相同的原因,不需要这样做


    最后,为了便于提取到
    Project
    类型的对象中,建议您使用运算符重载实现自己的提取程序。它保持
    void FileToStructProfessors()
    {
        int n;
        fstream CurrentFile("professor.txt");
        if (CurrentFile.fail())
        {
            cout <<"can't open file";
            // Put a return here so you don't
            // try to `.close()` the file later.
            return;
        }
        else
        {
            while(CurrentFile.good())
            {
                getline(CurrentFile, P1[n].FirstName);
                getline(CurrentFile, P1[n].LastName);
    
                // PID is an integer, and getline doesn't work with integers,
                // only strings. You can try simply 'CurrentFile >> P1[n].PID;',
                // but this will mess up the next getline (try it yourself if you
                // want). We can get around this using a stringstream, so put
                // '#include <sstream>' at the top of your file.
                string s;  
                getline(CurrentFile, s);
                istringstream iss(s);
                iss >> P1[n].PID;
    
                getline(CurrentFile, P1[n].Major);
                getline(CurrentFile, P1[n].Level);
                getline(CurrentFile, P1[n].Date);
    
                // This is to read past the blank line that
                // separates your entries.
                getline(CurrentFile, s);
    
                // Print stuff.
    
                n++;               
            }
        }
    
        CurrentFile.close();
    }
    
    while (  CurrentFile >> P1[n].FirstName &&
             CurrentFile >> P1[n].LastName  &&
             CurrentFile >> P1[n].PID       &&
             CurrentFile >> P1[n].Major     &&
             std::getline(CurrentFile >> std::ws, P1[n].Level) &&
             std::getline(CurrentFile >> std::ws, P1[n].Date) )
    
    CurrentFile >> size;
    std::vector<Professor> P1(size);
    
    CurrentFile.ignore(std::numeric_limits<std::streamsize>::max(), '\n');