17

我正在尝试开发一个简单的基于文本的刽子手游戏,主游戏循环以提示输入每个字母的猜测开始,然后继续检查该字母是否在单词中,如果是则结束生命不是。但是,当我运行游戏时,每次都会出现两次提示,并且程序不会等待用户输入。它还夺走了一条生命(如果输入正确,则一条生命,如果不是,则两条生命),因此无论它接受什么都与先前的输入不同。这是我的游戏循环,稍微简化了一点:

while (!finished)
{
    printf("Guess the word '%s'\n",covered);

    scanf("%c", &currentGuess);

    i=0;
    while (i<=wordLength)
    {
        if (i == wordLength)
        {
            --numLives;
            printf("Number of lives: %i\n", numLives);
            break;
        } else if (currentGuess == secretWord[i]) {
            covered[i] = secretWord[i];
            secretWord[i] = '*';
            break;
        }
        ++i;
    }

    j=0;
    while (j<=wordLength)
    {
        if (j == (wordLength)) {
            finished = 1;
            printf("Congratulations! You guessed the word!\n");
            break;
        } else {
            if (covered[j] == '-') {
                break;
            }
        }
        ++j;

        if (numLives == 0) {
            finished = 1;
        }

    }
}

我认为问题是 scanf 认为它在没有的情况下吸收了一些东西,但我不知道为什么。有谁有想法吗?我在 Mac OS X 10.5 上使用 gcc 4.0.1。

4

10 回答 10

22

当您使用 读取键盘输入时scanf(),在按下回车后会读取输入,但回车键生成的换行符不会被调用消耗scanf()。这意味着下次您从标准输入读取时,将有一个换行符在等待您(这将使下一次scanf()调用立即返回而没有数据)。

为避免这种情况,您可以将代码修改为:

scanf("%c%*c", &currentGuess);

匹配单个字符,但星号表示该%*c字符不会存储在任何地方。这具有消耗由回车键生成的换行符的效果,以便下次您调用时,scanf()您将从一个空的输入缓冲区开始。

警告: 如果用户按下两个键然后按下回车键,scanf()将返回第一个键击,吃掉第二个键,并为下一个输入调用留下换行符。scanf()像这样的怪癖是许多程序员回避朋友的原因之一。

于 2009-11-03T21:35:21.437 回答
8

换行符。

第一次通过循环,scanf() 读取字符。然后它读取换行符。然后它读取下一个字符;重复。

怎么修?

我很少使用 scanf(),但如果你使用格式字符串"%.1s",它应该跳过空格(包括换行符)然后读取非空格字符。但是,它将期望一个字符数组而不是单个字符:

char ibuff[2];

while ((scanf("%.1s", ibuff) == 1)
{
    ...
}
于 2009-11-03T20:13:18.710 回答
6

将问题分解成更小的部分:

int main(void) {
    char val;
    while (1) {
        printf("enter val: ");
        scanf("%c", &val);
        printf("got: %d\n", val);
    }
}

这里的输出是:

enter val: g
got: 103
enter val: got: 10

为什么scanf要给你另一个'10'呢?

由于我们为我们的值打印了 ASCII 数字,因此 ASCII 中的“10”是“输入”,因此scanf还必须抓住“输入”键作为字符。

果然,看着你的scanf字符串,你每次通过你的循环都要求一个字符。控制字符也被视为字符,并将被拾取。例如,您可以在上述循环中按“esc”然后按“enter”并获得:

enter val: ^[
got: 27
enter val: got: 10
于 2009-11-03T20:16:17.550 回答
3

只是一个猜测,但是您正在使用 scanf 输入单个字符,但用户必须键入猜测加上换行符,该换行符被用作单独的猜测字符。

于 2009-11-03T20:12:28.260 回答
3
scanf(" %c", &fooBar);

注意前面的空格%c。这很重要,因为它匹配所有前面的空格。

于 2011-09-15T01:13:37.820 回答
2

吉姆和乔纳森说得对。

为了让你的 scanf 行做你想做的事(使用换行符而不把它放在缓冲区中)我会把它改成

scanf("%c\n", &currentGuess);

(注意\n

不过,对此的错误处理非常糟糕。至少您应该对照 1 检查 scanf 的返回值,如果输入没有返回,则忽略输入(带有警告)。

于 2009-11-03T20:21:50.113 回答
1

我注意到几点:

  • scanf("%c")将读取 1 个字符并将 ENTER 保留在输入缓冲区中,以便下次通过循环
  • 即使i从用户读取的字符与secretWord
  • 什么covered[j]时候成为'-'?
于 2009-11-03T20:17:40.357 回答
1

我猜:当您输入数据时,您的代码会将换行符视为猜测之一。由于无法控制的错误处理,我一直避免使用 *scanf() 系列。尝试改用 fgets(),然后取出第一个字符/字节。

于 2009-11-03T20:18:18.880 回答
0

我在您的代码中看到了几件事:

  1. scanf 返回它读取的项目数。您可能希望处理它返回 0 或 EOF 的情况。
  2. 我的猜测是用户正在按字母 + Enter 并且您将换行符作为第二个字符。一个简单的检查方法是添加一个调试 printf 语句来显示输入的字符。
  3. 您的代码将只匹配第一次出现的匹配字母,即如果单词是“test”并且用户输入了“t”,那么您的代码将只匹配第一个“t”,而不是两者。你需要调整你的第一个循环来处理这个问题。
于 2009-11-03T20:20:46.713 回答
0

当您输入字符时,您必须输入一个空白字符才能继续。此空白字符存在于输入缓冲区、stdin文件中,并由scanf()函数读取。这个问题可以通过消耗这个额外的字符来解决。这可以通过使用getchar()函数来完成。

scanf("%c",&currentGuess);  
getchar();   // To consume the whitespace character.

我宁愿建议您避免使用scanf(),而是使用getchar(). scanf()需要大量的内存空间。getchar()是一个光照函数。所以你也可以使用 -

char currentGuess;  
currentGuess=getchar();  
getchar();  // To consume the whitespace character.
于 2010-10-03T04:33:28.323 回答