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

Wrong Inputs will cause the program to exit

本文关键字:程序 退出 输入 错误      更新时间:2023-10-16

看看这段代码:

#include <iostream>
using namespace std;
int main()
{
    string s;
    int n;
    float x;
again:
    cout << "Please Type this: ABC456 7.8 9 XYZn";
    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;
}

只需尝试键入"ABC456 7.8 9 XYZ"并按回车键,它将导致程序退出,然后提示用户重试。我知道输入是错误的,它们不在它们的类型中,但为什么它会导致退出?以及如何避免这种退出?

更改

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

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

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

ABC456 7.8 9 XYZ

s得到ABC456n得到7(因为它是int类型,输入缓冲区仍然有.8 9 XYZn(。接下来n .8,最后s "9"。现在输入缓冲区已包含XYZn。下次当您将输入读入t以获得用户的选择时,X 被读入t并且由于它不是yY,循环退出。

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。要确认,只需在输入t后插入另一个调试行cout << "nt = " << t;。因此,if为假,t的值分配给"X",因此程序退出。

如果你输入ABC456 7.8 9 YZ程序将再次要求输入,就像现在t一样,会有"Y"。

回答 https://stackoverflow.com/a/10379322/924727 expalins 会发生什么。关于为什么,我们必须稍微了解一下哲学。

C++流模型不被认为是"人类交互":它是将几乎无限的字符序列转换为空间分隔的"值"列表的通用转换器,以转换为提供的"变量"。

没有"对话的输入和输出交错"的概念。如果将输入写入文本文件,例如 myinput.txt(取消正确的输入(

ABC456 9 7.8 XYZ
Y
ABC456 5 6.7 XYZ
N

并从命令提示符下打开您的程序,例如

   myprogram < myinput.txt

您的程序将运行...并且不需要"暂停"来查看输出,因为没有人坐在那里查看并回答它。

程序暂停等待用户输入不是因为cin >>,而是因为cin未处于失败状态,缓冲区为空,缓冲区重新映射的源是控制台。在返回之前等待""的是控制台,而不是 cin。

cin >> n被调用时...

  • 调用operator>>函数,它...
  • 从流区域设置中获取num_get方面,并调用其 get 函数...
  • 重复调用流缓冲区sbumpc以获取数字并计算数字值。
  • 如果缓冲区有内容,它只是一个接一个地返回其字符。当没有更多的字符存在时(或者如果它是空的(......
  • 缓冲区要求操作系统从低级文件中读取。
  • 如果文件是控制台,则调用控制台内部行编辑器:
  • 这使得控制台卡住,让用户按字符和一些控件(例如退格键(,直到按 Enter 时
  • 控制台行编辑器将行返回到操作系统,该操作系统将允许内容可用于输入 CON 文件...
  • 它由填充自身的缓冲区读取(在将读取字符传递到 cvt 区域设置方面之后,但这是一个细节(。
  • 现在自己做回报展开。

所有这些机制使得这样一个事实:如果您键入的次数超过要求,缓冲区内容仍然可用于接下来的>>调用,无论它是否是另一个程序行。

适当的"更安全"解析要求在读取输入后,清除流状态,并在下一'n'之前忽略以下内容。这通常通过

cin.clear(); 
cin.ignore(numeric_limits<std::streamsize>::max(), 'n');
这样,键入

的任何内容都会被丢弃,下一个cin>>找到一个没有数据的缓冲区(只有'n',它被修剪为"开始空间"(,从而导致控制台再次进入行编辑模式。

当流提取运算符>>遇到无效输入时,它会将流置于不再提取输入的模式。

您可以使用布尔测试(如 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' );
}

有关更深入的内容,请参阅basic_ios上的标志语义(我敢肯定,这个网站上有几个问题与此完全相同(。

程序在

尝试将错误的数据类型放在一起时遇到问题或异常。也许您想查看 cin 上>> 运算符的文档,查找有关它在数据类型输入错误时执行的操作的详细信息,并查看 cin>> 行和您的输入以查找可能发生这种情况的任何位置,以便您可以验证输入是否正确处理<</p>

div class="answers">

当您调用 cin.clear(( 时, 你应该同时调用cin.sync((。

一旦流检测到错误,它就会处于错误状态,并且所有进一步尝试输入将是无操作。 访问由流而不首先测试读取是否成功是否未定义行为,所以至少在理论上,你的程序可以做任何事情。 (在实践,如果未初始化的变量是 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;
}