为什么 stream::good 是错误的示例?
Example of Why stream::good is Wrong?
我给出了一个答案,我想通过循环每次检查流的有效性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
blah
ends in new line
第二个(打印正确,以最后一行结尾):
bleck 1
blee 1 2
blah
this doesn't end in a new line
第三个是一个空文件(打印正确,一个换行符。)
第四个是丢失的文件(这正确打印了任何内容。)
谁能帮我举个例子来说明为什么 good
-不应该进行测试?
他们错了。口头禅是'never test .eof()
'.
- Why is iostream::eof inside a loop condition considered wrong?
即使是这个咒语也有点过分了,因为两者都有助于在提取失败后诊断流的状态。
所以口头禅应该更像
Don't use good()
or eof()
to detect eof before you try to read any further
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
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():
这证明它无法正常工作,因为永远不应读取空白文件。知道这一点的唯一方法是使用读取条件,因为流在第一次读取时总是好的。
使用 foo.good()
只是告诉您上一个读取操作工作得很好,下一个也可能工作。 .good()
检查流在给定点的状态。它不检查是否到达文件末尾。假设在读取文件时发生了一些事情(网络错误,os 错误,...) 好事将失败。这并不意味着已到达文件末尾。然而,当到达文件末尾时 .good() 失败,因为流无法再读取。
另一方面,.eof()
检查是否确实到达了文件末尾。
因此,.good()
可能会在未到达文件末尾时失败。
希望这可以帮助您理解为什么使用 .good()
检查文件末尾是一个坏习惯。
这已在其他答案中涵盖,但为了完整起见,我将简要介绍一下。与
唯一的功能区别
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);
}
是前者会在文件中没有行的情况下进行额外的循环,使得这种情况与一个空行的情况无法区分。我认为这不是通常 期望的行为。但我想这是见仁见智的事情。
正如 sehe 所说,口头禅是 overboard。这是一个简化。真正的重点是你不能 consume()
在测试失败之前读取流的结果或者至少是 EOF(并且在读取 之前的任何测试 都是无关紧要的) .这是人们在循环条件下测试 good()
时很容易做的事情。
但是,关于 getline()
的事情是它在内部测试 EOF,对于您和 returns 一个空字符串,即使只读取 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()
中有一个 "correct" 版本的读取循环 。这就是为什么使用 readline 至少部分满足规则,即使 outer 循环不符合。
但是,对于其他阅读方式,打破规则的伤害更大。考虑:
while(foo.good()) {
int bar;
foo >> bar;
consume(bar);
}
不仅您总是获得额外的迭代,而且该迭代中的bar
未初始化!
所以,简而言之,while(foo.good())
在您的情况下是可以的,因为 getline()
与某些其他读取函数不同,它在读取 EOF 位后使输出处于有效状态。并且因为您不关心甚至不希望文件为空时进行额外的迭代。
让我明确地说 是正确的。
但是 Nathan Oliver, Neil Kirk, and user2079303 提出的选项是使用 readline
作为循环条件而不是 good
。为了后代需要解决。
我们将问题中的循环与以下循环进行比较:
string bar;
while (getline(foo, bar)){
cout << bar << endl;
}
因为 getline
returns istream
作为第一个参数传递,并且因为当 istream
被转换为 bool
it returns !(fail() || bad())
,并且由于读取 EOF 字符将设置 failbit
和 eofbit
这使得 getline
成为有效的循环条件。
然而,当使用 getline
作为条件时,行为确实发生了变化,因为如果读取仅包含 EOF 字符的行,循环将退出,从而阻止输出该行。这不会出现在示例 2 和 4 中。但是示例 1:
bleck 1
blee 1 2
blah
ends in new line
使用 good
循环条件打印:
bleck 1
blee 1 2
blah
ends in new line
但是用 getline
循环条件截断最后一行:
bleck 1
blee 1 2
blah
ends in new line
示例 3 是一个空文件:
在 good
条件下打印:
在 getline
条件下不打印任何内容。
这些行为都没有错。 但是最后一行代码可能会有所不同。希望这个答案对您在出于编码目的在两者之间做出决定时有所帮助。
我给出了一个答案,我想通过循环每次检查流的有效性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
blah
ends in new line
第二个(打印正确,以最后一行结尾):
bleck 1
blee 1 2
blah
this doesn't end in a new line
第三个是一个空文件(打印正确,一个换行符。)
第四个是丢失的文件(这正确打印了任何内容。)
谁能帮我举个例子来说明为什么 good
-不应该进行测试?
他们错了。口头禅是'never test .eof()
'.
- Why is iostream::eof inside a loop condition considered wrong?
即使是这个咒语也有点过分了,因为两者都有助于在提取失败后诊断流的状态。
所以口头禅应该更像
Don't use
good()
oreof()
to detect eof before you try to read any further
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
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():
这证明它无法正常工作,因为永远不应读取空白文件。知道这一点的唯一方法是使用读取条件,因为流在第一次读取时总是好的。
使用 foo.good()
只是告诉您上一个读取操作工作得很好,下一个也可能工作。 .good()
检查流在给定点的状态。它不检查是否到达文件末尾。假设在读取文件时发生了一些事情(网络错误,os 错误,...) 好事将失败。这并不意味着已到达文件末尾。然而,当到达文件末尾时 .good() 失败,因为流无法再读取。
另一方面,.eof()
检查是否确实到达了文件末尾。
因此,.good()
可能会在未到达文件末尾时失败。
希望这可以帮助您理解为什么使用 .good()
检查文件末尾是一个坏习惯。
这已在其他答案中涵盖,但为了完整起见,我将简要介绍一下。与
唯一的功能区别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);
}
是前者会在文件中没有行的情况下进行额外的循环,使得这种情况与一个空行的情况无法区分。我认为这不是通常 期望的行为。但我想这是见仁见智的事情。
正如 sehe 所说,口头禅是 overboard。这是一个简化。真正的重点是你不能 consume()
在测试失败之前读取流的结果或者至少是 EOF(并且在读取 之前的任何测试 都是无关紧要的) .这是人们在循环条件下测试 good()
时很容易做的事情。
但是,关于 getline()
的事情是它在内部测试 EOF,对于您和 returns 一个空字符串,即使只读取 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()
中有一个 "correct" 版本的读取循环 。这就是为什么使用 readline 至少部分满足规则,即使 outer 循环不符合。
但是,对于其他阅读方式,打破规则的伤害更大。考虑:
while(foo.good()) {
int bar;
foo >> bar;
consume(bar);
}
不仅您总是获得额外的迭代,而且该迭代中的bar
未初始化!
所以,简而言之,while(foo.good())
在您的情况下是可以的,因为 getline()
与某些其他读取函数不同,它在读取 EOF 位后使输出处于有效状态。并且因为您不关心甚至不希望文件为空时进行额外的迭代。
让我明确地说
但是 Nathan Oliver, Neil Kirk, and user2079303 提出的选项是使用 readline
作为循环条件而不是 good
。为了后代需要解决。
我们将问题中的循环与以下循环进行比较:
string bar;
while (getline(foo, bar)){
cout << bar << endl;
}
因为 getline
returns istream
作为第一个参数传递,并且因为当 istream
被转换为 bool
it returns !(fail() || bad())
,并且由于读取 EOF 字符将设置 failbit
和 eofbit
这使得 getline
成为有效的循环条件。
然而,当使用 getline
作为条件时,行为确实发生了变化,因为如果读取仅包含 EOF 字符的行,循环将退出,从而阻止输出该行。这不会出现在示例 2 和 4 中。但是示例 1:
bleck 1
blee 1 2
blah
ends in new line
使用 good
循环条件打印:
bleck 1
blee 1 2
blah
ends in new line
但是用 getline
循环条件截断最后一行:
bleck 1
blee 1 2
blah
ends in new line
示例 3 是一个空文件:
在 good
条件下打印:
在 getline
条件下不打印任何内容。
这些行为都没有错。 但是最后一行代码可能会有所不同。希望这个答案对您在出于编码目的在两者之间做出决定时有所帮助。