为什么流:: good是错的?

时间:2015-02-03 13:09:37

标签: c++ file-io stream filestream

我给出了一个答案,我希望每次通过循环here检查流的有效性。

我的原始代码使用了good,看起来与此相似:

ifstream foo("foo.txt");

while (foo.good()){
    string bar;
    getline(foo, bar);
    cout << bar << endl;
}

我立刻被指出here并被告知从不测试good。很明显,这是我无法理解的,但我想正确地进行文件I / O.

我用几个示例测试了我的代码,无法使good - 测试代码失败。

首先(正确打印,以新行结尾):

  

bleck 1
  blee 1 2
  等等
  以新的一行结束
  

第二个(正确打印,以最后一行结尾):

  

bleck 1
  blee 1 2
  等等   这不会以新的一行结束

第三个是空文件(打印正确,只有一个换行符。)

第四个是丢失的文件(这没有正确打印。)

有人可以通过一个演示为什么 good的示例来帮助我 - 不应该进行测试吗?

4 个答案:

答案 0 :(得分:6)

他们错了。口头禅永远不会测试.eof()&#39;。

即使这种口头禅太过分了,因为两者都有助于在提取失败后诊断流的状态。

所以咒语应该更像是

  

在您尝试继续阅读之前,请不要使用good()eof()来检测eof

fail()bad()

相同

当然,在使用流之前可以有效地使用stream.good(例如,如果流是尚未成功打开的文件流)

然而,两者都经常 滥用 来检测输入的结束,而不是它的工作方式。


一个典型的例子,说明为什么你不应该使用这种方法:

std::istringstream stream("a");
char ch;
if (stream >> ch) {
   std::cout << "At eof? " << std::boolalpha << stream.eof() << "\n";
   std::cout << "good? " << std::boolalpha << stream.good() << "\n";
}

打印

false
true

查看 Live On Coliru

答案 1 :(得分:2)

其他答案已经涵盖了这一点,但为了完整起见,我将简要介绍一下。

唯一的功能差异
while(foo.good()) { // effectively same as while(foo) {
    getline(foo, bar);
    consume(bar); // consume() represents any operation that uses bar
}

while(getline(foo, bar)){
    consume(bar);
}

当文件中没有行时,前者会做一个额外的循环,使得这种情况与一个空行的情况无法区分。我认为这不是通常期望的行为。但我认为这是意见问题。

正如他所说,咒语是 overboard 。这是一个简化。真正重要的是,在测试失败或至少EOF(以及读取之前的任何测试无关紧要)之前,您不得consume()读取流的结果。这是人们在循环条件下测试good()时容易做的事情。

但是,关于getline()的问题是,它会在内部测试EOF,即使只读取了EOF,也会返回一个空字符串。因此,前一个版本可能大致类似于跟随伪c ++:

while(foo.good()) {
    // inside getline
    bar = "";               // Reset bar to empty
    string sentry;
    if(read_until_newline(foo, sentry)) {
        // The streams state is tested implicitly inside getline
        // after the value is read. Good
        bar = sentry        // The read value is used only if it's valid.
    // ...                  // Otherwise, bar is empty.
    consume(bar);
}

我希望这说明了我想说的话。可以说在 getline()内有一个“正确”的读取循环版本。这就是为什么即使外部循环不符合规范,使用readline也至少部分地满足了规则。

但是,对于其他阅读方法,违反规则会受到更多伤害。考虑:

while(foo.good()) {
    int bar;
    foo >> bar;
    consume(bar);
}

总是总是获得额外的迭代,该迭代中的bar未初始化!

因此,简而言之,while(foo.good())在您的情况下是可以的,因为getline()与某些其他读取函数不同,在读取EOF位后将输出保留为有效状态。并且因为当文件为空时你不在乎甚至不希望额外的迭代。

答案 2 :(得分:1)

good()eof()都会在您的代码中为您提供额外的一行。如果您有一个空白文件并运行:

std::ifstream foo1("foo1.txt");
std::string line;
int lineNum = 1;

std::cout << "foo1.txt Controlled With good():\n";
while (foo1.good())
{
    std::getline(foo1, line);
    std::cout << lineNum++ << line << std::endl;
}
foo1.close();
foo1.open("foo1.txt");
lineNum = 1;

std::cout << "\n\nfoo1.txt Controlled With getline():\n";
while (std::getline(foo1, line))
{
    std::cout << line << std::endl;
}

您将获得的输出是

foo1.txt Controlled With good():
1

foo1.txt Controlled With getline():

这证明它无法正常工作,因为永远不应该读取空白文件。知道这一点的唯一方法是使用读取条件,因为流在​​第一次读取时始终是好的。

答案 3 :(得分:1)

使用foo.good()只是告诉您上一次读取操作工作正常,下一次操作也可以正常工作。 .good()检查给定点的流状态。它不检查是否到达文件末尾。让我们说在读取文件时发生的事情(网络错误,操作系统错误,......)好将失败。这并不意味着文件的结尾已经到达。然而.good()在到达文件末尾时失败,因为流不再能够读取。

另一方面,.eof()检查文件的末尾是否真正到达。

因此,.good()可能会在未到达文件末尾时失败。

希望这可以帮助您理解为什么使用.good()检查文件末尾是一个坏习惯。