C ++输入失败:单个语句中的多个输入

时间:2015-03-24 23:19:08

标签: c++

我的C ++教授坚持认为,在检查输入失败时,必须为每个输入使用单独的while()循环。他表示,检查在单个cin语句中收集的多个输入的以下方法不起作用:

while (!good){
    cout << "Enter the length and the width of the rectangle: ";
    cin >> length >> width;
    if(!cin){
        cout << "Bad input, try again" << endl;
        cin.clear();
        cin.ignore(200, '\n');}
    else {
        good = true;}}

他提议的方法:

bool good = false;
while (!good){
    cout << "Enter the length rectangle: ";
    cin >> length;
    if(!cin){
        cout << "Bad input, try again" << endl;
        cin.clear();
        cin.ignore(200, '\n');}
    else {
        good = true;}}  
while (good){
    cout << "Enter the width rectangle: ";
    cin >> width;
    if(!cin){
        cout << "Bad input, try again" << endl;
        cin.clear();
        cin.ignore(200, '\n');}
    else {
        good = false;}}

我的上述方法似乎工作得很好。如果为任一输入输入了非数字字符,则循环将提示输入新的输入并清除故障状态。

像我一样检查输入失败只是简单的错误形式吗?我希望更清楚地了解为什么我的方法是错误的。如果我的方法有问题,有没有办法检查输入失败而不为每个用户输入使用单独的循环?我主要问的是因为我编写的程序涉及从文件中获取许多输入,并且单独检查每个变量似乎过于繁琐。我认为必须有更好的方法。

2 个答案:

答案 0 :(得分:3)

两者都是&#34;正确&#34;因为他们从失败的输入中恢复并给用户另一次机会。

两者仅对交互式输入有用。你提到从文件中读取 - 真的无法恢复。忽略损坏的字段只会导致下一个字段被消耗,而的解释与文件中的位置指示的方式不同。从文件中读取时的最佳操作方法是输出尽可能好的解释(错误发生在文件中的确切位置,例如行和列号,预期的数据类型以及遇到的数据无效)

在交互式使用中,清除缓冲区并再次提示是有用的..你的版本的缺点是,在正确输入长度后,如果用户指责宽度,他们就不能重新输入错误数据,但也必须第二次输入长度。

然而,第二次编写循环是非常无意义的。相反,你应该写一个辅助函数,如:

template<typename T>
T prompt_for_value( const char* const prompt )
{
    T result;
    while (true) {
         std::cout << prompt << std::flush;
         if (std::cin >> result) return result;
         std::cout << "Bad input, try again" << std::endl;
    }
}

double width = prompt_for_value<double>("Enter the width in meters: ");
double height = prompt_for_value<double>("Enter the height: ");

请注意,代码总体上较短,避免了good变量的笨拙使用,该变量在原始代码的中途颠倒了它的含义。此外,呼叫站点现在非常干净,完全专注于重要信息 - 输入的提示和数据类型。

感谢C ++ 11 lambda支持,它现在也很容易为辅助函数添加参数验证:

T prompt_for_value( const char* const prompt, std::function<bool(T)> validation = {} )
{
    T result;
    while (true) {
         std::cout << prompt << std::flush;
         if (std::cin >> result) {
             if (validation && !validation(result)) {
                 std::cout << "Input out of range, try again" << std::endl;
                 continue;
             }
             return result;
         }
         std::cout << "Bad input, try again" << std::endl;
    }
}

double width = prompt_for_value<double>("Enter the width in meters: ",
                                        [](int w) { return w >= 0; });
double height = prompt_for_value<double>("Enter the height: ",
                     [&width](int h) { return (h >= 0) && (h <= 10000.0 / width); }));

答案 1 :(得分:0)

由于违反了代码中的don't-repeat-yourself (DRY) principle,您所表示的教授提出的方法非常糟糕。如果你想多次做同样的事情,你应该使用循环或函数来避免编写重复的逻辑。

您的方法涉及全有或全无检查,因此可能需要用户重复自己。这种可用性应根据具体情况决定。

两种方法都无法在输入行的末尾检测到无关的字符。这样的输入可能被合理地解释为错误 - 无论它是否恰好与后续输入请求一起使用。

然而,正如您已经证明的那样,全有或全无的方法确实基本上起作用。鉴于此,这是基于iostream library能够进行的基于自动异常的检查的替代全有或全无实现。

   cin.exceptions( ~std::ios::goodbit );

   int length=0, width=0;

   for ( bool ok=false; !ok; )
      {
      try
         {

         cout << "Enter the length and the width of the rectangle: ";
         cin >> std::skipws >> length >> width;

         std::string s;
         std::getline( cin, s );
         s.erase( std::remove_if( s.begin(), s.end(), ::isspace ), s.end() );
         if ( !s.empty() ) throw std::runtime_error("too much input");

         ok = true;
         }
      catch( std::ios::failure const & e )
         {
         cout<<"\n"<<"bad input ["<<e.what()<<"], try again...\n";
         cin.clear();
         cin.ignore( std::numeric_limits<std::streamsize>::max(), '\n' );
         }
      catch( std::runtime_error const & e )
         {
         cout<<"\n"<<e.what()<<", try again...\n";
         }
      }

   cout<<"\n"<<"length="<<length<<", width="<<width<<"\n";

输出:

Enter the length and the width of the rectangle: x

bad input [basic_ios::clear], try again...
Enter the length and the width of the rectangle: 1 x

bad input [basic_ios::clear], try again...
Enter the length and the width of the rectangle: 1 2 x

too much input, try again...
Enter the length and the width of the rectangle: 1 2 3

too much input, try again...
Enter the length and the width of the rectangle: 4 5

length=4, width=5