如何检查cin上读取的数据类型?

时间:2013-10-29 05:57:18

标签: c++ input types

更具体地说,我想确保这个类型是double(或int / float)。我已经近距离搜索,发现几乎没有非严格的解决方案。我想只使用我能完全理解的代码,作为C ++的完全初学者。

在Java中,我可以简单地做这样的事情:

ArrayList<Double> data = new ArrayList<Double>();
Scanner in = new Scanner(System.in);
while (in.hasNextDouble()) {
        double x = in.nextDouble();
        data.add(x); 
        n += 1;      
        sum += x;    
}  
double average = sum / n; 

C ++显然是一个不同的故事。这是我试过的:

vector<double> data;
while (looping) 
{
    cin >> x;
    if (typeid(x) != typeid(float) && typeid(x) != typeid(double) &&
        typeid(x) != typeid(int))
            {looping = false; break;}
    data.push_back(x);
    n += 1;
    sum += x;
}
double average = sum / n;

这不起作用。循环在输入双精度时继续运行,但是一旦我输入的东西不是双精度,代码就会停止并且不会继续前进。我很沮丧。有什么建议吗?

3 个答案:

答案 0 :(得分:3)

用户不“输入变量”。她输了一个字符串。当提供float变量时,运算符>>尝试将此字符串解释为浮点数的文本表示。

它可能成功也可能不成功:在Java中,失败会导致nextDouble()抛出异常。在C ++中,流操作不会抛出;相反,失败意味着cin.good()将开始返回false

float x;
cin >> x;
while (cin.good()) 
{   
    data.push_back(x);
    n += 1;
    sum += x;
    cin >> x;
}

答案 1 :(得分:0)

如果x被声明为double,则if条件将失败,因此while循环被破坏

答案 2 :(得分:0)

这里有几个解决方案。我会发布其中2个。由于stod函数,第一个解决方案需要c ++ 11标准。您可以将-std=c++11标记传递给gccclang++。 Microsoft编译器默认启用c ++ 11。

解决方案1 ​​。它包括通过cin >> input_string和使用标准c ++ 11函数stod一直读取字符串。 stod代表字符串加倍。如果stod无法解析double,则会引发std::invalid_argument例外。

这个解决方案是这样的:

#include <iostream>
#include <vector>
#include <numeric>
#include <string>
#include <stdexcept>

using namespace std;


int main() {
   vector<double> vec;
   string input;
   try {
      while (getline(cin, input)) {
         vec.push_back(stod(input));

      }
   }
   catch (std::invalid_argument &) {
      cout << "Invalid argument caught\n";
      //Failed to parse a double
   }
   //If pressed Ctrl+D (in linux, which sends EOF), failbit and eofbit are set
   //If it is through invalid_argument the way the loop was exit, then,
   //eof and failbit are not set and cin can be used without clearing.
   double average = accumulate(vec.begin(), vec.end(), 0.0)/vec.size();
   cout << "EOF: " << cin.eof() << endl;
   cout << "Fail: " << cin.fail() << endl;

   //Clean eof and failbit to be able to use cin again
   cin.clear();
   cout << "EOF after cleaning: " << cin.eof() << endl;
   cout << "Fail after cleaning: " << cin.fail() << endl;

   cout << average << endl;

}

编辑:我测试过,当你每行输入多个数字时,它只会得到第一个没有抛出std::invalid_argument的数字。只有当行以非双精度开始行时,它才会抛出std::invalid_argument。这是因为stod函数的行为如下:stod reference

请注意,此解决方案仅允许每行读取一个双倍。

解决方案2 。直接使用cin >> input_double阅读。这可能会失败。请注意,iostream在c ++中默认不使用exceptios。您可以使用api激活它们,但我不建议您这样做,因为您可以在本地管理所有错误处理。

您可以阅读由任意空格字符分隔的任意数量的双精度数:

#include <iostream>
#include <vector>
#include <numeric>
#include <limits>

using namespace std;


int main() {
   double x = 0.0;
   vector<double> data;

   //cin will fail to read the first non-double in the input.
   //You can input as many doubles as you wish. Spaces will
   //be ignored. At the first non-double, cin will fail to read
   //and will exit the loop, setting istream::failbit.
   //Alternatively, you can send EOF (Linux is Ctrl+D) and the loop also will finish.
   while (cin >> x) {
      data.push_back(x);

   }
   double average = accumulate(data.begin(), data.end(), 0.0)/data.size();

   //If you wanted to use again cin, you should:
   //1. Clear failbit. You can do like this:
   cin.clear();

   //2. Cleaning the remaining input. Will stop when finding end of line.
   string rubbish;
   geline(cin, rubbish);
   //cin is usable here again if you need it and with the input clean already.


   cout << average << '\n';
}

你可以在输入中用一行代表这样的东西:

1 2.4 -38.7 5.8 28.9你好。

会发生什么?循环将消耗到28.9,停在你好。之后,设置failbit。我们清理failbit以便能够继续阅读直到行结束。因为hello被认为是“垃圾”,因为我们想读取双打,我们用getline清理它,我们可以再次使用cin而不会有麻烦。