我的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;}}
我的上述方法似乎工作得很好。如果为任一输入输入了非数字字符,则循环将提示输入新的输入并清除故障状态。
像我一样检查输入失败只是简单的错误形式吗?我希望更清楚地了解为什么我的方法是错误的。如果我的方法有问题,有没有办法检查输入失败而不为每个用户输入使用单独的循环?我主要问的是因为我编写的程序涉及从文件中获取许多输入,并且单独检查每个变量似乎过于繁琐。我认为必须有更好的方法。
答案 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