从中读取文件时查找文件结尾

Tyl*_*aff 1 c++ fstream

void graph::fillTable()
{
  ifstream fin;
  char X;
  int slot=0;

  fin.open("data.txt");

  while(fin.good()){

  fin>>Gtable[slot].Name;
  fin>>Gtable[slot].Out;
  cout<<Gtable[slot].Name<<endl;
  for(int i=0; i<=Gtable[slot].Out-1;i++)
    {
      **//cant get here**
    fin>>X;
    cout<<X<<endl;
    Gtable[slot].AdjacentOnes.addFront(X);
    }
  slot++;
  }
 fin.close();
}
Run Code Online (Sandbox Code Playgroud)

这是我的代码,基本上它完全符合我的要求但是当文件不再好的时候它会继续阅读.它会输入并输出我正在寻找的所有东西,然后当文件结束时,fin.good()显然不会返回false.这是文本文件.

A 2 B F

B 2 C G

C 1 H

H 2 G I

I 3 A G E

F 2 I E
Run Code Online (Sandbox Code Playgroud)

这是输出

A
B
F
B
C
G
C
H
H
G
I
I
A
G
E
F
I
E

Segmentation fault
Run Code Online (Sandbox Code Playgroud)

-

这是Gtable的类型.

struct Gvertex:public slist
  {
    char Name;
    int VisitNum;
    int Out;
    slist AdjacentOnes;
    //linked list from slist
  };
Run Code Online (Sandbox Code Playgroud)

我希望它在输出'E'后停止,这是文件中的最后一个字符.在读完最后一个字符后,程序永远不会再次进入for循环.我无法弄清楚为什么不打破.

Jam*_*nze 5

你在while循环中的条件是错误的. ios::eof()不是预测性的; 只有当流尝试(内部)读取超出文件末尾时才会设置它.你必须在每次
输入后检查.

处理你的案例的经典方法是定义一个>> 函数GTable,类似于:

std::istream&
operator>>( std::istream& source, GTable& dest )
{
    std::string line;
    while ( std::getline( source, line ) && line.empty() ) {
    }
    if ( source ) {
        std::istringstream tmp( line );
        std::string name;
        int count;
        if ( !(tmp >> name >> count) ) {
            source.setstate( std::ios::failbit );
        } else {
            std::vector< char > adjactentOnes;
            char ch;
            while ( tmp >> ch ) {
                adjactentOnes.push_back( ch );
            }
            if ( !tmp.eof() || adjactentOnes.size() != count ) {
                source.setstate( std::ios::failbit );
            } else {
                dest.Name = name;
                dest.Out = count;
                for ( int i = 0; i < count; ++ i ) {
                    dest.AdjacentOnes.addFront( adjactentOnes[ i ] );
                }
            }
        }
    }
    return source;
}
Run Code Online (Sandbox Code Playgroud)

(这是相当仓促的.在实际代码中,我几乎肯定会将内部循环分解为一个单独的函数.)

注意:

  • 我们逐行阅读,以验证格式(并在出现错误时允许重新同步).

  • 我们failbit在输入错误的情况下设置源流.

  • 我们跳过空行(因为你的输入显然包含它们).

  • 在确定输入正确之前,我们不会修改目标元素.

我们有一个,很容易遍历所有元素:

int slot = 0;
while ( slot < GTable.size() && fin >> GTable[ slot ] ) {
    ++ slot;
}
if ( slot != GTable.size )
    //  ... error ...
Run Code Online (Sandbox Code Playgroud)

编辑:

我会明确地指出这一点,因为其他人的反应似乎已经错过了:尝试阅读之前确保你有阅读的地方是绝对必要的.

编辑2:

鉴于这个问题收到的错误答案的数量,我想强调:

  • 在输入已知失败fin.eof() 之前的任何使用都是错误的.

  • 任何使用fin.good()期间都是错误的.

  • 在测试输入成功之前使用其中一个值是错误的.(这不会阻止类似的事情fin >> a >> b,只要在测试成功之前都没有a或者b使用过.)

  • 任何试图在Gtable[slot]不确保slot 界限的情况下进行阅读都是错误的.

关于eof()good():

基类的istreamostream定义了三个"错误"位:failbit,badbiteofbit.重要的是要了解何时设置:badbit在不可恢复的硬错误的情况下设置(实际上从来没有,实际上,因为大多数实现不能或不能检测到这样的错误); 并且failbit在任何其他情况下设置输入失败 - 无数据可用(文件结束)或格式错误("abc"输入int时等). eofbit任何时候设置streambuf返回EOF,无论这是否导致输入失败!因此,如果你读了一个int,并且流包含"123",而不是尾随空格或换行符, eofbit将被设置(因为流必须提前读取以知道 int结束的位置); 如果流包含"123\n",eofbit则不会设置.但是,在这两种情况下,输入都成功,并且failbit不会设置.

要读取这些位,有以下函数(作为代码,因为我不知道如何获取表):

eof():   returns eofbit
bad():   returns badbit
fail():  returns failbit || badbit
good():  returns !failbit && !badbit && !eofbit

operator!():      returns fail()
operator void*(): returns fail() ? NULL : this
    (typically---all that's guaranteed is that !fail() returns non-null.)
Run Code Online (Sandbox Code Playgroud)

鉴于此:第一次检查必须始终是(fail()operator基于fail)之一.一旦fail()返回true,我们可以使用其他函数来确定原因:

if ( fin.bad() ) {
    //  Serious problem, disk read error or such.
} else if ( fin.eof() ) {
    //  End of file: there was no data there to read.
} else {
    //  Formatting error: something like "abc" for an int
}
Run Code Online (Sandbox Code Playgroud)

实际上,任何其他用途都是错误(任何使用good() 都是错误 - 不要问我为什么函数在那里).