试图了解cin行为

时间:2017-04-12 12:30:29

标签: c++ iostream

我正在编写从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

有人可以解释一下这里发生了什么吗?

4 个答案:

答案 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/