在下面的代码中
for (int i = 0; i < lInput; i++)
{
cout << "Enter a score: ";
cin >> *(lPtr + i);
if (*(lPtr + i) < 0 || *(lPtr + i) > 100)
{
cout << "Invalid input, enter again: ";
}
}
问题在于,在输入无效时,您会输出包含错误消息并提示用户输入新数字的文本,但您实际上并没有读取任何新输入。相反,您只需跳转到下一个循环迭代,这实际上意味着您正在接受错误的输入。
解决问题的一种方法是创建一个无限循环,该循环将继续运行,直到用户输入有效输入。发生这种情况时,您可以使用 break 语句跳出该循环。
for (int i = 0; i < lInput; i++)
{
for (;;) //infinite loop, equivalent to while(1)
{
cout << "Enter a score: ";
cin >> *(lPtr + i);
if (*(lPtr + i) < 0 || *(lPtr + i) > 100)
{
cout << "Input must be between 0 and 100, try again!\n";
continue;
}
break;
}
}
但是,此代码的一个问题是它只执行范围检查,而根本不检查输入是否有效。特别是,它不会检查流提取运算符>> 是否成功地将用户的输入转换为数字。这可以通过调用cin.fail()来检查。
最好在范围检查之前执行此附加检查,如下所示:
for (int i = 0; i < lInput; i++)
{
//this loop will continue until the input is valid
for (;;) //infinite loop, equivalent to while(1)
{
cout << "Enter a score: ";
cin >> *(lPtr + i);
//check if stream error occurred
if ( cin.fail() )
{
//check if error is recoverable
if ( cin.bad() )
{
throw std::runtime_error( "unrecoverable I/O error" );
}
//print error message
cout << "Input must be a number, try again!\n";
//discard bad input (remainder of line)
cin.ignore( std::numeric_limits<std::streamsize>::max(), '\n' );
//clear stream status flags
cin.clear();
continue;
}
if (*(lPtr + i) < 0 || *(lPtr + i) > 100)
{
cout << "Input must be between 0 and 100, try again!\n";
continue;
}
//input is valid, so break out of the infinite loop
break;
}
}
请注意,上面的代码需要您另外#include <limits>。
但是,这段代码仍然不是很完美。如果输入12sdlhfh 等输入,那么它将接受12 作为有效输入,但下一次流提取将失败,因为sdlhfh 不是有效数字,并且会打印错误消息。可以通过在每次提取流后丢弃该行的其余部分来防止此错误消息,但在这种情况下,这可能不是理想的解决方案,因为您可能希望拒绝输入,例如 12sdlhfh。
为了能够拒绝此类输入,您不应使用流提取运算符>>,因为它会在遇到非数字时立即停止读取。相反,您应该始终使用std::getline 一次读取一行,并使用std::stoi 和一些额外的代码来验证整行,以验证数字后没有出现非空白字符。
for (int i = 0; i < lInput; i++)
{
//this loop will continue until the input is valid
for (;;) //infinite loop, equivalent to while(1)
{
std::string line;
std::size_t pos;
cout << "Enter a score: ";
getline( cin, line );
//check if stream error occurred
if ( cin.fail() )
{
//check if error is recoverable
if ( cin.bad() )
{
throw std::runtime_error( "unrecoverable I/O error" );
}
//print error message
cout << "Input error, try again!\n";
//clear stream status flags
cin.clear();
continue;
}
//attempt to perform the actual conversion
try
{
*(lPtr + i) = std::stoi( line, &pos );
}
catch ( std::invalid_argument )
{
cout << "Unable to convert input to number, try again!\n";
continue;
}
catch ( std::out_of_range )
{
cout << "Range error, try again!\n";
continue;
}
//verify that rest of line does not contain any non-whitespace characters
for ( ; pos < line.length(); pos++ )
{
if ( !std::isspace( static_cast<unsigned char>(line[pos]) ) )
{
cout << "Invalid character found, try again!\n";
//we cannot use "continue" here, because that would
//continue to the next iteration of the innermost
//loop, but we want to continue to the next iteration
//of the outer loop
goto continue_outer_loop;
}
}
if (*(lPtr + i) < 0 || *(lPtr + i) > 100)
{
cout << "Input must be between 0 and 100, try again!\n";
continue;
}
//input is valid, so break out of the infinite loop
break;
continue_outer_loop:
continue;
}
}
请注意,上面的代码还需要:#include <string> 和 #include <cctype>。
上面的代码使用了一个goto 语句。通常,如果可能,您不应使用goto,but for exiting nested loops, it is acceptable。
另外请注意,如果您使用上述代码,那么它将与在循环外使用cin >> lInput; 不兼容。混合std::getline 和std::istream::operator>> 通常不会起作用,因为std::istream::operator>> 会将换行符留在缓冲区中,所以下一次调用std::getline 可能只会检索一个空行。