while(!inStream.eof()){
int data;
// yay, not end of stream yet, now read ...
inStream >> data;
// oh crap, now we read the end and *only* now the eof bit will be set (as well as the fail bit)
// do stuff with (now uninitialized) data
}
反对:
int data;
while(inStream >> data){
// when we land here, we can be sure that the read was successful.
// if it wasn't, the returned stream from operator>> would be converted to false
// and the loop wouldn't even be entered
// do stuff with correctly initialized data (hopefully)
}
while( !in.eof() )
{
int data;
in >> data;
if ( in.fail() ) /* Handle with break or throw */;
// Now use data
}
在这里,in.fail()验证只要有东西要读,它就是正确的,它的目的不仅仅是一个 while 循环终止符。 到目前为止一切顺利,但是如果流中有尾随空格会发生什么听起来像是反对eof()作为终结符的主要顾虑? 我们不需要放弃错误处理;把空白都吃掉
while( !in.eof() )
{
int data;
in >> data >> ws; // Eat white space with 'std::ws'
if ( in.fail() ) /* Handle with 'break' or 'throw' */;
// Now use data
}
while( !(in>>ws).eof() )
{
int data;
in >> data;
if ( in.fail() ) /* Handle with 'break' or 'throw' */;
/* This will never fire if the eof is reached cleanly */
// Now use data
}
while(!stream.eof())
{
stream >> n;
//some work on n;
}
这里的问题是,如果不首先检查流读取是否成功,就不能执行some work on n,因为如果不成功,some work on n将产生不希望的结果。 关键在于,eofbit、badbit或failbit是在**尝试从流中读取之后设置的。**因此,如果stream >> n失败,则立即设置eofbit、badbit或failbit,因此,如果您将while (stream >> n)因为如果从流中阅读时出现故障,则返回的对象stream转换为false,因此循环停止;如果读取成功,则返回的对象stream转换为true,循环继续。
5条答案
按热度按时间a2mppw5e1#
因为
iostream::eof
只会在阅读完流的末尾之后返回true
,它并不表示下一次读取将是流的末尾。考虑这个(并假设下一次读取将在流的末尾):
反对:
关于第二个问题:因为
等于
并且不与
iq3niunx2#
**底线顶端:**通过正确处理白色,下面是
eof
的使用方法(甚至在错误检查方面比fail()
更可靠):(* 感谢托尼·D提出的突出显示答案的建议。请看他下面的评论,其中有一个例子说明为什么这样做更有效。)
反对使用
eof()
的主要论点似乎忽略了白色的一个重要的微妙之处,我的主张是,显式检查eof()
不仅不是“ 总是错误的 *"--这似乎是这个问题和类似的堆栈溢出问题中压倒一切的观点--而且通过正确处理空格,它提供了一个更干净、更可靠的错误处理。并且是“总是正确的”解决方案(尽管不一定是最简洁的)。以下是建议的“适当”终止和读取顺序的总结:
由于超出eof的读取尝试而导致的失败被视为终止条件。这意味着没有简单的方法来区分成功的流和由于eof以外的原因而真正失败的流。以下列流为例:
1 2 3 4 5<eof>
1 2 a 3 4 5<eof>
a<eof>
while(in>>data)
终止于一个设置failbit
,用于所有三个输入。在第一个和第三个输入中,eofbit
也被设置。所以在循环之后,需要非常难看的额外逻辑来区分正确的输入(第一个)和不正确的输入(第二个和第三个)。然而,请看以下内容:
在这里,
in.fail()
验证只要有东西要读,它就是正确的,它的目的不仅仅是一个 while 循环终止符。到目前为止一切顺利,但是如果流中有尾随空格会发生什么听起来像是反对
eof()
作为终结符的主要顾虑?我们不需要放弃错误处理;把空白都吃掉
std::ws
在设置eofbit
时跳过流中任何潜在的(零或更多)尾随空格,而不是failbit
。因此,只要至少有一个数据要读取,in.fail()
就按预期工作。如果也可以接受全空流,则正确的格式为:**摘要:**正确构造的
while(!eof)
不仅是可能的,也是正确的,而且它允许数据在作用域中本地化,并提供了错误检查与常规业务的更清晰分离。也就是说,while(!fail)
无疑是一个更常见、更简洁的习惯用法,在简单(每个读取类型一个数据)的场景中可能是首选。o7jaxewo3#
因为如果程序员不写
while(stream >> n)
,他们可能会写:这里的问题是,如果不首先检查流读取是否成功,就不能执行
some work on n
,因为如果不成功,some work on n
将产生不希望的结果。关键在于,
eofbit
、badbit
或failbit
是在**尝试从流中读取之后设置的。**因此,如果stream >> n
失败,则立即设置eofbit
、badbit
或failbit
,因此,如果您将while (stream >> n)
因为如果从流中阅读时出现故障,则返回的对象stream
转换为false
,因此循环停止;如果读取成功,则返回的对象stream
转换为true
,循环继续。1l5u6lss4#
其他答案已经解释了为什么
while (!stream.eof())
中的逻辑是错误的以及如何修复它。为什么显式使用
iostream::eof
检查eof是错误的?一般来说,检查
eof
only 是错误的,因为流提取(>>
)可能会失败,而不会到达文件末尾。如果您有int n; cin >> n;
,而流包含hello
,则h
不是有效数字,因此提取将失败,而不会到达输入末尾。此问题与尝试读取流之前检查流状态的一般逻辑错误(这意味着对于N个输入项,循环将运行N+1次)结合在一起,导致以下症状:
>>
将失败(没有输入可读取),所有应该设置的变量(由stream >> x
)实际上都未初始化。这导致处理垃圾数据,这可能表现为无意义的结果(通常是巨大的数字)。(If您的标准库符合C++11,现在情况有点不同:失败的
>>
现在会将数值变量设置为0
,而不是让它们处于未初始化状态(char
除外)。>>
操作都失败,变量很可能会保留前一次迭代中的值。这可能表现为“最后一行打印两次”或“最后一个输入记录处理两次”。(This C++11之后应该表现得有点不同(见上文):现在你得到了一个零的“幻影记录”,而不是重复的最后一行。)
.eof
,则会以无限循环结束。>>
将无法从流中提取任何数据,因此循环会原地旋转,而不会到达结尾。总结一下:解决方案是测试
>>
操作本身是否成功,而不是使用单独的.eof()
方法:while (stream >> n >> m) { ... }
,就像在C中测试scanf
调用本身是否成功一样:while (scanf("%d%d", &n, &m) == 2) { ... }
.xt0899hw5#
需要记住的重要一点是,
inFile.eof()
直到***尝试读取失败后***才变成True
,因为你已经到达了文件的末尾,所以,在这个例子中,你会得到一个错误。正确执行此循环的方法是将阅读和检查合并为一个操作,如下所示
按照约定,
operator>>
返回我们从中读取的流,当流失败时(例如到达文件末尾),对流进行布尔测试将返回False
。所以这给了我们正确的顺序:
如果您碰巧遇到一些 * 其他 * 问题,使您无法正确阅读文件,那么您将无法访问
eof()
。让我们通过一个示例来跟踪上述代码的工作过程
'1', '2', '3', 'a', 'b'
。a
。a
提取为int时,它将失败。clear
流,否则从它阅读的所有尝试都将失败。False
,因为我们还没有到达文件末尾,因为还有a
等待读取。但是,如果我们使用这样的循环,我们将得到所需的输出。
在这种情况下,流将转换为
False
,不仅在文件结束的情况下,而且在转换失败的情况下,例如a
,我们不能读取为整数。