在一个带有“是”或“否”结尾的do while循环中使用if语句

15

我是编程新手,正在尝试做一个长的do while循环,其中嵌套了if语句,但是我在让我的循环真正循环方面遇到了问题。

与其直接在我的项目上寻求帮助(该项目代码非常长),我创建了一个简单的类似版本,但它也无法循环。它会到达结尾并询问用户是否想要再试一次,但当输入"y"时,它会忽略if语句。

#include <iostream>
#include <string>
using namespace std;

int main()
{
    string sodaChoice;
    char answer = 'n';

    do
    {
        cout << "Please choose your favorite soda from the following: Rootbeer, Diet Coke, Coke, Sprite" << "\n";
        getline(cin, sodaChoice);

        if (sodaChoice == "Rootbeer")
        {
            cout << "Me too!" << "\n";
        }
        else if (sodaChoice == "Diet")
        {
            cout << "Not me :(" << "\n";
        }
        else if (sodaChoice == "Coke")
        {
            cout << "It's alright" << "\n";
        }
        else if (sodaChoice == "Sprite")
        {
            cout << "only if I'm sick" << "\n";

        }
        else
        {
            cout << "That was not on the list";
        }
        cout << "would you like to try again?";
        cin >> answer;



    } while (answer == 'y'|| answer == 'Y');




    return 0;
}

我想也许我需要在if语句周围的do循环中嵌套一个do循环,但我不知道该如何去做。任何帮助都将不胜感激。我已经花费了很多很多小时来尝试解决它。

我试图询问我的老师,但是我的大学教授一些基于其书籍的通用课程,由他们的院长编写。他对于帮助或者教学并不是非常热情。

编辑: 感谢各位的答案! 关于这个问题是否重复,我还没有学习cin.ignore,因此我不清楚它与我的问题有关。谢谢你们教给我!


我喜欢我的令人愉快的感谢和祝贺被删除的方式,以向OP表达他们的MCVE。我猜它违反了行为准则。 - Lightness Races in Orbit
@user202729 不可能知道何时阅读评论并因此不再需要。在仅几个小时(最多)后匆忙删除无害评论是有害的。 - Lightness Races in Orbit
@user202729 哦,我很久以前就做过了。这是一个无望的事情! - Lightness Races in Orbit
4个回答

13

随着

cin >> answer;

您读取了一个字符,问题是在写答案时输入了至少两个字符。实际的'y''n'加上Enter键,它会作为新行添加到输入缓冲区中。

然后,下一个getline调用将读取此换行符作为空行。

有几种解决此问题的方法,例如在读入answer后使用ignore。或者,如果您只想要单词输入,则对于sodaChoice也可以使用格式化输入,因为它默认跳过前导空格(如换行符)。


@user202729 是的,使用 >> 进行格式化输入默认会跳过前导空格。但是字符后面的换行符是一个尾随空格,std::getline 不会跳过它。 - Some programmer dude

7

对于用户行为良好,并输入了一个 'y' 或 'n',然后跟着一个换行符的情况,现有答案是正确的。

我们可以通过修改 .ignore() 调用来增加一些安全性,要求它忽略直到并包括下一个换行符之前的所有字符:

您需要在翻译单元顶部添加此头文件包含:

#include <limits>

cin >> answer之前,您需要添加以下行:

// consumes and ignores as many characters as necessary until a newline. 
// The newline is also consumed.
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

这里有完整的程序和测试输入:

http://coliru.stacked-crooked.com/a/996e77559590ad6d

不幸的是,计算机科学老师对计算机科学一无所知并不罕见。不要担心或因此而放弃学习如何编写软件 - 这只是一个事实的症状,即优秀的软件工程师比优秀的教师获得更高的薪酬。

这里有一些有用的参考资料:https://en.cppreference.com/w/cpp/io/basic_istream/ignore


6
我会给出与其他答案完全相反的建议。不要使用cin.ignore跳过你读取后面的任何额外字符,我认为最好使用getline()进行所有读取,并完全忘记cin >> somevar
原因如下:
1. 使用cin >> somevar获得的格式化输入会忽略前导空格,包括换行符。如果用户在空行上按Enter键,则程序不执行任何操作,但仍然期望进一步输入。这不是CLI程序用户的期望!通常期望的是Enter完成输入,而空输入应该导致程序使用默认值,或者抱怨无效输入。使用cin >> somevar,你不能做到这两点。
2. 格式化输入会在缓冲区中保留任何未匹配的输入。在你的示例中,读取单个字符至少会留下以下换行符,但其他类型的输入也会发生同样的情况。读取数字(使用int i; cin >> i;)会将数字之后的任何内容等待下一个输入操作,因此输入123 abc将返回123,并且abc将出现在下一个输入操作中。
这背后的原因是,命令行终端默认处于基于行的模式。程序或库函数在输入完整行之前不会看到来自操作系统的任何输入。(cin.ignore()当然可以帮助你处理这个问题,但无法解决第一个问题。)
使用getline()恰好匹配底层机制,让你处理空输入(以及在读取int时区分零和非数字),并自动处理尾部垃圾。不过,你需要手动将读取的字符串转换为实际想要的内容。
在你的情况下,你可以轻松地将循环条件更改为以下内容:
string answer;
...
    getline(cin, answer);
} while (answer == "y" || answer == "Y");

如果您需要一个数字,您可以使用例如int num = std::stoi(str)
(在C语言中,如果使用scanf()进行用户输入,同样最好使用fgets(),然后使用sscanf()解析字符串,这个方法提供了相同的选项,但处理行缓冲。我对C++不太熟悉,所以无法确定如何使用getline()读取完整行并与cin >> somevar具有相同的等效性。)

非常感谢!我也很喜欢其他的答案,但是我们正在遵循的课程不允许我们在程序中使用我们还没有“学习”的东西,所以如果我想要避免失分,就不能使用cin.ignore。 - JKisa
@JKisa,唉。我深表同情。我想你可以通过手动重新实现等效的 cin.ignore 来解决这个问题。只需编写一个循环,每次读取一个字符,直到看到换行符为止。 - ilkkachu

0

由于您正在从类型为char的用户接收输入,但每次敲击Enter键时,它会将换行符(\n)赋值给变量answer

您可以通过在输入行后添加一行来忽略此问题。

cin.ignore();

您可以阅读this以获取更多详细信息。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接