看看这段代码:
#include <iostream>
using namespace std;
int main()
{
string s;
int n;
float x;
again:
cout << "Please Type this: ABC456 7.8 9 XYZ\n";
cin >> s >> n >> x >> s;
cout << "\nDo you want to try Again(y/n)? ";
char t;
cin >> t;
if (t=='y' || t=='Y')
goto again;
return 0;
}
尝试输入&#34; ABC456 7.8 9 XYZ&#34;然后按Enter键,它将导致程序退出,然后提示用户再次尝试。我知道输入是错误的,它们不属于它们的类型,但它为什么会导致退出?以及如何避免这种退出?
答案 0 :(得分:7)
更改
cin >> s >> n >> x >> s;
到
cin >> s >> x >> n >> s;
当您输入7.8
作为第二个输入时,但是您在整数变量而不是浮点变量中收集它。因此,当您输入:
ABC456 7.8 9 XYZ
s
获取ABC456
,n
获取7
(因为它是int类型,输入缓冲区中仍然包含.8 9 XYZ\n
)。下一个n
获取.8
,最后s
获得"9"
。现在输入缓冲区中有XYZ\n
。下次当您将输入读入t
以供用户选择时,X
会被t
读入,因为它不是y
或Y
,循环退出。
答案 1 :(得分:3)
在cin
cout << "s = " << s << " n = " << n << " x = " << x;
然后运行
Please Type this: ABC456 7.8 9 XYZ
ABC456 7.8 9 XYZ
s = 9 n = 7 x = 0.8
显然,第一个ABC456被读入字符串s
。下一个是整数,因此只有7
被读入n
而0.8
部分被读入float
x
。现在,下一个输入9
再次分配给s
,因此s
的最终值是字符串“9”。现在,X
的第一个字符会被输入下一个cin
,并将其分配给t
。输入cout << "\nt = " << t;
后确认只插入另一个调试行t
。因此,if
为false,t
的值分配给'X',因此程序退出。
如果您输入ABC456 7.8 9 YZ
,程序会再次要求输入,因为现在t
将有'Y'。
答案 2 :(得分:2)
当流提取操作符>>
遇到无效输入时,它会将流放入不再提取输入的模式。
您可以使用布尔测试(例如if ( cin )
)检测此模式,并使用cin.clear()
重置它。执行此操作后,无效输入仍保留在cin
的输入缓冲区中,因此您需要以某种方式处理它,例如ignore
。
更好的是,提取运算符返回cin
,因此您可以在提取时进行测试:
if ( ! ( cin >> s >> n >> x >> s ) ) {
cout << "You fail horribly!\n";
cin.clear();
cin.ignore( std::numeric_limits< std::streamsize >::max(), '\n' );
}
有关更深入的信息,请参阅 Semantics of flags on basic_ios (我确信本网站上有一些问题与此完全重复)。
答案 3 :(得分:2)
回答https://stackoverflow.com/a/10379322/924727 expalins会发生什么。 关于为什么,我们必须进入哲学领域。
C ++流模型不被认为是“人类交互”:它是一个几乎无限的字符序列的通用转换器,被转换为提供的 space 分隔的“值”列表“变量”。
没有“对话框的输入和输出交错”的概念。
如果您将输入写入文本文件,如myinput.txt
(未正确输入)
ABC456 9 7.8 XYZ
Y
ABC456 5 6.7 XYZ
N
并从命令提示符启用您的程序,如
myprogram < myinput.txt
你的程序将运行......并且不需要“暂停”来查看输出,因为没有人坐在那里看到并回答它。
程序暂停等待用户输入不是因为cin >>
,而是因为cin
未处于失败状态且缓冲区为空且缓冲区重新映射的源是控制台。控制台在返回之前等待'\ n',而不是cin。
调用cin >> n
时......
operator>>
函数,它...... sbumpc
以获取数字并计算数字值。所有这些机制都会产生这样的事实 - 如果您输入的内容超过了要求 - 缓冲区内容仍然可用于下一个>>
调用,它是否是另一个程序行。
正确的“更安全”解析需要,在读取输入后,要清除流状态,并忽略以下内容直到下一个'\n'
。
这通常用
cin.clear();
cin.ignore(numeric_limits<std::streamsize>::max(), '\n');
这样,丢弃任何已输入的内容,下一个cin>>
找到一个没有数据的缓冲区(只有'\n'
,它被修剪为“起始空间”),从而导致控制台再次进入行编辑模式。
答案 4 :(得分:1)
当程序尝试但错误的数据类型在一起时会遇到问题或异常。也许您想查看&gt;&gt;的文档。 cin上的运算符,查找有关数据类型输入错误输入时的操作细节,并查看cin&gt;&gt;对于可能发生这种情况的任何地方的行和输入,以便您可以验证输入是否正确处理
答案 5 :(得分:1)
当你调用cin.clear()时,你应该同时调用cin.sync()。
答案 6 :(得分:1)
一旦流检测到错误,它就处于错误状态,并且全部
进一步尝试输入将是no-ops。访问由a读取的变量
没有首先测试读取是否成功的流是未定义的
行为,至少在理论上,你的程序可以做任何事情。 (在
实践中,如果未初始化的变量属于char
类型,那么您将面临所有风险
是一个随机值。)
当读取面向行的输入时,最好的解决方案是使用
std::getline
。然后使用输入的字符串构造一个
std::istringstream
来解析这一行。这使得输入流进入
一个好状态,已经同步下一个输入。我用了
类似的东西:
void
getInput()
{
std::string line;
std::cout << "Please type this: ABC456 7.8 9 XYZ" << std::endl;
std::getline( std::cin, line );
if ( ! std::cin ) {
// Very unexpected error...
throw SomethingSerious();
}
std::string s;
int n;
float f;
std::istringstream toParse( line );
toParse >> s >> f >> n >> s;
if ( ! toParse ) {
// Input incorrect...
}
}
bool
tryAgain()
{
std::cout << "Do you want to try again (y/n)? ";
std::string line;
std::getline( std::cin, line );
return line.size() == 1 && (line[0] == 'y' || line[0] == 'Y');
// But I'd be more tolerant with regards to spaces...
}
bool
oneInput()
{
getInput();
return tryAgain();
}
int
main()
{
while ( oneInput() ) {
}
return 0;
}