我正在编写从stdin获取(巨大)输入流并将其读入浮点数向量的软件。我想捕获流包含逗号等字符的情况,并且要么无法接受它,要么只是忽略无法解析为float的所有内容(无论哪个更容易实现,我都没有偏好)。我注意到以下行为:当我打电话
时echo "1.4, -0.7 890 23e-3" | ./cintest
此版本
#include <iostream>
using std::endl;
using std::cin;
using std::cout;
int main ( int argc, const char* argv[] ){
float val;
while (cin >> val) {
cout << val << endl;
}
return 0;
}
打印
1.4
这个版本
#include <iostream>
using std::endl;
using std::cin;
using std::cout;
int main ( int argc, const char* argv[] ){
float val;
while (cin) {
cin >> val;
cout << val << endl;
}
return 0;
}
打印
1.4
0
没有逗号,第一个打印
1.4
-0.7
890
0.023
而第二个打印
1.4
-0.7
890
0.023
0.023
有人可以解释一下这里发生了什么吗?
答案 0 :(得分:3)
代码的第一个版本
while (cin >> val) {
尝试解析一个浮点数,然后检查流状态是否良好。 (具体来说,它会调用operator>>
进行提取,这会在出错时设置failbit
,然后使用bool conversion来测试failbit
。)
因此,如果流状态不好(因为它无法将,
转换为浮点数),则不会输入循环体。因此,它会在第一次失败的转换时终止。
第二个版本
while (cin) {
cin >> val;
检查流状态是否良好(它只是告诉你之前的转换成功),然后尝试解析一个浮点数,然后假定这成功而不检查。它应该在转换之前检查流状态,然后再使用浮点值,在这种情况下,浮点值将从上一次迭代中遗留下来。
在正确的实现中,当转换失败时,您应检查fail()
是否为真,但eof()
为假(即,由于文件结束之外的某些原因,转换失败)。在这种情况下,使用ignore()
来丢弃输入 - 您可能需要空格(并忽略到下一个空格),或者只是忽略一个字符并重试。
请注意,上面链接的ignore
文档包含具有正确错误处理的示例代码。如果我们选择在失败的转化中跳过单个字符,您的代码将变为:
for(;;) {
float val;
std::cin >> val;
if (std::cin.eof() || std::cin.bad()) {
break;
} else if (std::cin.fail()) {
std::cin.clear(); // unset failbit
std::cin.ignore(1); // skip next char
} else {
std::cout << val << '\n';
}
}
答案 1 :(得分:2)
>>
失败时,您的结果与有关。
在两个版本中,您都会读取您的值并达到逗号(或EOF)。读取显然失败,因为,
和EOF不是>>
可以解析的有效整数。所以>>
的返回值(流本身)转换为false
,然后退出第一个版本的循环(这就是它应该如何工作)。
然而,在第二个版本中(通常不应该这样做),您仍在打印val
中的任何值。在C ++ 11之前的C ++中,val
保持不变;因为C ++ 11 >>
在失败时写0
。
TL; DR:您的第二个版本会将循环停止,并写入一遍垃圾。
答案 2 :(得分:1)
这是因为在第二个你有一个错误。
您应该始终检查格式化的输入operator>>
是否确实有效。
所以这段代码:
cin >> val;
cout << val << endl;
应写成:
if (cin >> val) {
cout << val << endl;
}
如果operator>>
失败。然后它将设置流上的一个失败位,而不是将任何值放入val
。所以没有必要打印val
,因为没有任何内容。
这就是为什么你的第二个版本在没有数据可供阅读时打印垃圾。读取失败,然后打印出一个值。然后你尝试重新启动循环(失败)。
第一个正常工作。
while (cin >> val) {
cout << val << endl;
}
因为您读取了一个值,然后在进入循环之前检查读取是否有效。
答案 3 :(得分:0)
std :: cin是std :: istream
的实例化当任何一点都有逗号或任何无效数据类型时,运营商&gt;&gt;失败。因此,您的代码会打印最后一个已知的'val'值。
此处链接是'std :: istream&gt;&gt;'
的参考http://www.cplusplus.com/reference/istream/istream/operator%3E%3E/