【问题标题】:how to reach the end of file如何到达文件末尾
【发布时间】:2014-01-11 06:03:11
【问题描述】:

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

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();
}

教授.txt

4

hamed
hamidi
1001
civil
associate professor
91/02/15

morteza
jafari
1005
computer
assistant professor
91/01/20

mahdi
foladi
1006
computer
professor
90/10/10

mostafa
mohammadi
1009
electronic
assistant professor
91/05/5

教授结构

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

【问题讨论】:

  • @user2589043 不要使用if(!currentFile),使用if(!currentFile.fail())
  • @user2589043 请试一试我的答案。
  • @Keeler 谢谢你,你帮了很多忙。

标签: c++ file struct


【解决方案1】:

您的问题是您正在阅读尝试阅读行,但CurrentFile &gt;&gt; 忽略了所有空格,包括空格。所以当你读到第一个教授时:

med
hamidi
1001
civil
associate professor
91/02/15

你会得到:

FirstName = med
LastName = hamidi
PID = 1001
Major = civil
Level = associate
Date = professor

'associate' 和 'professor' 之间有一个空格,所以CurrentFile &gt;&gt; P1[n].Level 只选择了 'associate' 因为CurrentFile &gt;&gt; 看到了空格并停下来。然后您的下一个CurrentFile &gt;&gt; 会选择“教授”而不是您要阅读的日期。在整个文件中复合,你会得到很多没有意义的输入。

您可以使用getline(CurrentFile, *string you want to read in*) 解决此问题,如以下示例所示。 请注意,这不是生产质量代码,只是一个简单的玩具示例,说明 getline 旨在帮助您推进项目。

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();
}

顺便说一句,我删除了:

  1. CurrentFile.seekp(5)
  2. CurrentFile.ignore(std::numeric_limits&lt;std::streamsize&gt;::max(), '\n');

注意

我的示例不安全/不正确,因为这些 getline 调用或 CurrentFile &gt;&gt; 语句中的任何一个都可能遇到错误,并且没有一个被检查错误。我写这篇文章是为了对您的代码出现问题的原因进行一个相对简单易懂的解释。对于安全/正确的代码,请查看0x499602D2's answer

【讨论】:

  • 这两个都表现出同样的问题。在读取失败之前文件不会坏,可能是由于 eof。
  • 好吧,我假设 1) 文件已正确打开,并且 2) 他/她知道文件中条目的数量和顺序,因此之前的读取次数与之前一样多EOF 被击中。我想看看这个文件真的很确定。
  • 你能把你打开的'professor.txt'文件的内容贴出来吗?
  • 我添加了文本文件。
  • @user2589043 好吧,我想您的代码中还有其他错误。欢迎来到精彩的编程世界!但是,您在问题中提到的问题已通过此处的答案解决。更准确地找出您当前的问题是什么,然后在需要更多帮助时提出一个新问题。
【解决方案2】:

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

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

这是使用您的代码表示的上述任​​务:

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) )

请注意,我还使用std::getline() 更改了最后两次提取。这是因为文件中的相应数据包含格式化运算符用作分隔符的空格。日期使用斜线,在使用格式化输入时也将其分隔。之后,LevelDate 数据成员必须是 std::strings 并且您需要使用 未格式化 输入。

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

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

【讨论】:

  • 这可能比我的例子更安全,但它不适用于这个文件。请参阅我的答案以了解原因(读取的教授“级别”中的空格)。
  • 您提到getline 用斜线分隔,但我认为这不准确。如果我没记错的话,默认情况下,对getline(someStream, someString); 的调用将分隔\n
  • @Keeler 我指的是格式化输入:cin &gt;&gt; someInt;。当遇到不符合该类型格式要求的字符时,流将停止提取。这样的字符可以是像'/' 这样的非整数字符。所以在某种程度上,流确实由这些字符类“定界”。但我理解在这种情况下它如何被视为一个不准确的词。
  • 完全正确。在您的回答中不清楚您指的是cin &gt;&gt; someInt,感谢您澄清您的意思。
【解决方案3】:

改进您的程序:

这是你问题的症结所在。您不了解流输入的意图是如何执行的。不知道谁教你while (!file.eof()),但几乎总是走错路。

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

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

为了避免这个潜在的问题,您可以使用像std::vector&lt;T&gt; 这样的动态容器。它将分配一个适当大小的内部数组,并在需要时动态调整大小。您可以在构造时指定尺寸,如下所示:

CurrentFile >> size;
std::vector<Professor> P1(size);

向这个容器添加数据就像P1.push_back(p) 一样简单。您不再需要计数器。


CurrentFile.seekp(5) 没有意义,因为流被用于输入并且只会影响输出指针。如果你想忽略第一个整数,你可以简单地提取到一个虚拟变量中。但既然我已经在上面做了,那就没有必要了。如果您试图忽略文件顶部与第一个 Professor 名称之间的整数之间的空格,则再次不需要这样做,因为格式化的提取器会自动丢弃输入序列中的前导空格。您不必担心无意中提取了空格。

这也使得以下代码变得不必要:

CurrentFile.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

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


最后,为了便于提取到 Project 类型的对象中,建议您使用运算符重载实现自己的提取器。它使您的其余代码保持干净和连贯。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-12-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多