【问题标题】:while loop to infinity when the input of a cin is a 'dot'当 cin 的输入是“点”时,while 循环到无穷大
【发布时间】:2011-12-13 10:38:37
【问题描述】:

我在使用 cin 方法获取变量时遇到问题。当输入是一个数字时没有问题,但是当它是一个像点[.]这样的特殊字符时, whileloop 循环到无穷大。 我做错了什么?

cout << "What is your race" <<endl<<"1.Human\n2.troll\n3.zombie"<<endl;
    cin >> *race;
    while(*race<1||*race>3)
    {
    system("cls");
    cout << "Wrong choice"<<endl<< "What is your race" <<endl<<"1.Human\n2.troll\n3.zombie"<<endl;
    cin >> *race;
    }

我搜索了答案,我应该刷新缓冲区,但我不知道该怎么做。我对 c++ 很陌生。谢谢

【问题讨论】:

  • @Rps:不,不是。如果raceint 类型,那么*race 将是一个错误,编译器会拒绝它。

标签: c++ loops while-loop cin


【解决方案1】:

race 设为字符,然后您将能够:

while (*race < '1' || *race > '3')

这可能是您想要实现的目标。

解释:

当您将cin &gt;&gt; 转换为 int 时,它会将给定的 ASCII 字符串转换为整数值。 . 没有整数含义,因此它不会被读入 race 并且 failbit 已设置 - 进一步的 >>s 是无操作的,直到您清除它们。但是,如果您将cin &gt;&gt; 转换为char 并将其与其他chars(实际上是它们的ASCII 码)进行比较,您将能够轻松检查它。

【讨论】:

  • 解决方案可能是真正想要的(尽管它不能解决所有问题),但解释是不正确的。当您将cin &gt;&gt; 转换为int,并且输入流包含'.' 时,failbit 被设置在cin 中,并且所有进一步的输入都变为无操作,直到它被清除。 (当然,他还需要删除违规字符;cin.ignore( INT_MAX, '\n' ); 可能最适合此。)如果cin 上出现任何其他错误,您的解决方案将进入无限循环,例如 end文件。
  • 嗯,看起来你通常是正确的,但在最后一个陈述中是错误的 - 你不会stdin获得EOF。
  • 从什么时候开始的?该标准当然支持它,并且我通过我所知道的所有实现都得到了它。当cin 遇到文件结束(如果不是文件结束条件)时会发生什么?
  • 呃,对,我的错。但是,其他可能的方法不会为您提供对 EOF 的支持,我知道有一种方法(因为 !file.eof() 不被认为是好的做法):std::getline()。根据您的评论,我应该告诉他“不要使用cin &gt;&gt;,使用std::getline()!” - 但在这种情况下似乎没有多大意义,因为它不是“如何处理 EOF”的问题,而是“如何处理 .”的问题。
  • 输入将在文件末尾失败。只需测试失败 (if (cin)) 和 失败后,检查它是否是由于文件结束,并做任何适当的事情。 (鉴于他正在提示,并且显然期待键盘输入,而不是从文件重定向的输入,在文件末尾抛出异常可能是合适的。)更一般地说,如果不使用 getline, you should check for failure in the loop (while (!cin...)` ),然后在继续之前清除错误,或者在文件结尾的情况下中止输入。
【解决方案2】:

这个例子完全重现了你的问题:

#include <iostream>

int main()
{
    int i = 5;
    while (i < 1 || i > 3)
    {
        std::cin >> i;
    }
}

会发生以下情况:当operator&gt;&gt; 无法读取整数时(例如,当您键入一个点并按 Enter 时),您键入的任何内容 都会保留 在流中,包括换行符。 因此,在 while 循环的下一次迭代中,下一个输入已经存在,并且由于它不是有效的整数,因此循环永远不会中断。 您需要确保在 operator&gt;&gt; 失败时清空流并清除所有设置的错误标志。

#include <iostream>
#include <limits>

int main()
{
    int i = 5;
    while (i < 1 || i > 3)
    {
        if (!(std::cin >> i))
        {
            // clear streams internal error flags
            std::cin.clear();
            // ignore what's left in the stream, up to first newline character
            // or the entire content, whichever comes first
            std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
        }
    }
}

【讨论】:

    【解决方案3】:

    您的代码存在几个问题。第一个是你不 验证您的输入是否成功;的正确条件 while 应该是:

    while ( !cin || (*race < 1 || *race > 3) )
    

    如所写,如果输入失败(当你 输入'.',假设race 的类型为int*),然后*race 包含它之前的值,不管它是什么。

    第二个是如果你确实收到了来自cin 的错误,你并不清楚 它。一旦流处于错误状态,它就会一直保持这种状态,直到您 明确清除它。如果cin已经失败,需要执行:

    cin.clear();
    

    循环中的某个地方。

    第三个是如果cin失败了,你不提取那个字符 使其失败,因此在清除错误状态后,您需要 提取它。鉴于您构建对话的方式,您可能 想要忽略所有内容,直到行尾:

    cin.ignore( INT_MAX, '\n' );
    

    即使cin 没有失败,您也可能希望在循环中执行此操作 (如果由于 *race &lt; 1 || *race &gt; 3 条件而输入),或者在 成功案例。或者,您可能想转向阅读线, 并确保该行仅在字符后包含空格 你有兴趣。

    最后一个解决方案是我会采用的,因为它处理得很好 以上所有问题。所以我的代码看起来像:

    //  return -1 on error in input,
    //  throw exception on (unexpected) end of file
    int
    getRace( std::istream& source )
    {
        std::string line;
        if ( !std::getline( source, line ) ) {
            throw std::ios_base::failure( "Unexpected end of file" );
        }
        std::istringstream tmp( line );
        int results;
        return tmp >> results >> std::ws && tmp.get() == EOF
            ? results
            : -1;
    }
    
    //  ...
    int race = -1;
    while ( race < 0 ) {
        std::cout << "What is your race\n"
                     "1. Human\n"
                     "2. Troll\n"
                     "3.  Zombie\n" << std::flush;
        race = getRace( std::cout );
        if ( race < 0 ) {
            std::cout << "Wrong choice" << std::endl;
        }
    }
    

    请注意,通过一行输入,您可以避免任何问题 重置格式错误,跳过错误输入或重新同步 错误的情况。

    【讨论】:

      【解决方案4】:

      除了接受的解决方案之外,另一种解决方案是清除 cin 的 failbit 并忽略最后一个输入,如下所示:

      cout << "What is your race" <<endl<<"1.Human\n2.troll\n3.zombie"<<endl;
      cin >> *race;
      while(*race<1||*race>3)
      {
          // Clears the state of cin to be in good state
          cin.clear();
          // Ignores the last input read so that it's not read back again
          cin.ignore();
          system("cls");
          cout << "Wrong choice"<<endl<< "What is your race" <<endl<<"1.Human\n2.troll\n3.zombie"<<endl;
          cin >> *race;
      }
      

      【讨论】:

      • 您要清除的不是badbit,而是failbit。 (但cin.clear() 将它们全部清除,并且是正确的解决方案。)
      • @JamesKanze:感谢您的更正。为对此解决方案感兴趣的人编辑了答案。
      猜你喜欢
      • 1970-01-01
      • 2013-06-05
      • 1970-01-01
      • 2017-03-08
      • 1970-01-01
      • 1970-01-01
      • 2023-02-07
      • 2020-09-12
      • 2017-06-21
      相关资源
      最近更新 更多