错误的输入将导致程序退出

时间:2012-04-30 06:14:04

标签: c++

看看这段代码:

#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键,它将导致程序退出,然后提示用户再次尝试。我知道输入是错误的,它们不属于它们的类型,但它为什么会导致退出?以及如何避免这种退出?

7 个答案:

答案 0 :(得分:7)

更改

cin >> s >> n >> x >> s;

cin >> s >> x >> n >> s;

当您输入7.8作为第二个输入时,但是您在整数变量而不是浮点变量中收集它。因此,当您输入:

ABC456 7.8 9 XYZ

s获取ABC456n获取7(因为它是int类型,输入缓冲区中仍然包含.8 9 XYZ\n)。下一个n获取.8,最后s获得"9"。现在输入缓冲区中有XYZ\n。下次当您将输入读入t以供用户选择时,X会被t读入,因为它不是yY,循环退出。

答案 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被读入n0.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>>函数,它......
  • 从流语言环境中获取num_get facet并调用其get函数...
  • 重复调用流缓冲区sbumpc以获取数字并计算数字值。
  • 如果缓冲区有内容,它只会一个接一个地返回其字符。当没有更多角色存在时(或者如果它是空的)......
  • 缓冲区要求操作系统从低级文件中读取。
  • 如果文件是控制台,则调用控制台内部行编辑器:
  • 这会让控制台卡住让用户按下字符和某些控件(例如退格键),直到按Enter键为止
  • 控制台行编辑器将该行返回给操作系统,该行将使内容可用于输入CON文件......
  • 缓冲区读取(将读取的字符传递给cvt语言环境facet,但这是一个细节),它会填满自己。
  • 现在给自己做一个展开的回报。

所有这些机制都会产生这样的事实 - 如果您输入的内容超过了要求 - 缓冲区内容仍然可用于下一个>>调用,它是否是另一个程序行。

正确的“更安全”解析需要,在读取输入后,要清除流状态,并忽略以下内容直到下一个'\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;
}
相关问题