c++ - 读取文件时找到文件末尾

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

那是我的代码,基本上它完全符合我的要求,但当文件不再有效时它会继续读取。它会输入和输出我要查找的所有内容,然后当文件结束时,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

这是输出

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

Segmentation fault

-

这是 Gtable 的类型。

struct Gvertex:public slist
  {
    char Name;
    int VisitNum;
    int Out;
    slist AdjacentOnes;
    //linked list from slist
  };

我希望它在输出“E”(文件中的最后一个字符)后停止。读取最后一个字符后,程序再也不会进入 for 循环。我不明白为什么 while 没有中断。

最佳答案

您在 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;
}

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

注意:

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

  • 我们在源流中设置了failbit以防输入错误。

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

  • 我们不会修改目标元素,直到我们确定输入 是正确的。

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

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

编辑:

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

编辑 2:

考虑到这个问题收到的错误答案的数量,我会 喜欢强调:

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

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

  • 在测试输入之前使用读取的值之一 已经成功是错误的。 (这不会阻止像 fin >> a >> 这样的事情 b,只要既不使用a也不使用b才成功 测试。)

  • 任何试图读入 Gtable[slot] 而未确保 slot 在边界内是错误的。

关于 eof()good():

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

要读取这些位,有以下函数(作为代码,因为我 否则不知道如何获得一张 table ):

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

鉴于此:第一个检查必须始终是 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
}

实际上,任何其他使用都是错误的(任何使用 good() 是一个错误——别问我为什么有这个函数)。

关于c++ - 读取文件时找到文件末尾,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8241792/

相关文章:

c++ - GTK 应用程序 : How do I create a working indicator with Qt/C++?

c++ - 将 unique_ptr<Derived<T>> 传递给函数

C++:从具有空字符的文件中读取

c++ - 使用 STL 在 C++ 中处理大于 2 GB 的文件

c++ - 在类中使用 fstream getline() 函数

c++ - 如何构建有或没有递归的非二叉树?

c++ - 写入文件后立即从文件中读取

c++ - 快速获得接近 2 的幂数( float )的方法

c++ - 从文件读入结构

c++ - "Updating"文件中的一行被覆盖