2015-03-24 93 views
1

我的C++教授坚持在检查输入失败时,必须为每个单独的输入使用单独的while()循环。他表示,检查多于一个输入以下方法聚集在一个cin说法是行不通的:C++输入失败:单个语句中有多个输入

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;}} 

我的方法似乎工作就好了。如果输入了任何一个输入的非数字字符,则循环将提示输入新的信息并清除故障状态。

它是否被认为是不正确的形式来检查输入失败?我希望更清楚地了解为什么我的方法是错误的。如果我的方法有问题,是否有办法检查输入失败,而不用为每个用户输入使用单独的循环?我主要问,因为我正在编写一个程序,涉及从文件中获取许多输入,并且单独检查每个变量似乎过于乏味。我在想,一定有更好的办法。

+0

你的教授需要了解这种新语法(只有40岁)被称为一个做一段时间。 – 2015-03-24 23:57:32

+0

为了更接近希腊化名:更多关注,两种代码都不检查“eof”标志。 – 2015-03-24 23:59:06

+0

您可能会发现[C++ FAQ:如何获得std :: cin来跳过无效输入字符?](https://isocpp.org/wiki/faq/input-output#istream-and-ignore)有用(请参阅第二个代码示例。) – 2015-03-25 00:02:24

回答

3

两者都是“正确的”,因为它们从失败的输入中恢复并给用户另一个机会。

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

在交互式使用中,清除缓冲区和再次提示很有用..您的版本具有的缺点是,在正确输入长度后,如果用户胖了手指的宽度,他们不能只是重新输入错误的数据,但也必须再次输入长度。

然而,第二次写入循环是难以置信的无意义的。相反,你应该写一个辅助函数,是这样的:

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的λ的支持,它现在也很容易参数验证添加到辅助函数:

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); })); 
+0

如果从文件读取,这将导致无限循环。 – 2015-03-25 00:14:02

+0

@remyabel:那么原创。我清楚地表示,这种重试方法仅适用于交互式输入。我对'cin'和'cout'进行了硬编码,以减少错误地将它用于文件输入的可能性。你有没有读到答案? – 2015-03-25 00:14:57

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 
+0

检查尾随字符是一个不错的主意,但异常处理增加了复杂性,没有任何好处。应该使用异常来实现来自调用堆栈中几层以外的集中式错误处理,而不是本地重试。 – 2015-03-25 02:48:32

+0

使用iostreams库很复杂,至少有可能打开异常使事情变得更简单 - 特别是如果你想执行多个I/O操作。使用异常让我更有信心,如果出现问题,我会知道它。我认为在这里使用异常而不是手动检查是没有什么坏处的 - 当然执行速度并不是问题。我的实现有更多的代码行,但主要是支持更多的错误条件检查。 – nobar 2015-03-25 06:46:50

+0

我喜欢指出iostreams的异常模式,尤其是因为对于基于文件的I/O,通过绕过手动错误检查,可以使得全部或全无的方法变得更清晰/更简单。我猜这种可比较复杂性的选择是在完成整个I/O序列之前忽略所有的错误,然后*只询问库是否真正起作用。也许这是一个合理的方法,但对我来说,它似乎不雅和容易出错。 – nobar 2015-03-25 13:17:01