在C中循环时,Scanf会相互跳过

时间:2009-11-03 20:04:20

标签: c gcc scanf

我正在尝试开发一个简单的基于文本的刽子手游戏,并且主游戏循环以提示输入每个字母的猜测开始,然后继续检查字母是否在单词中并且需要生命如果不是。但是,当我运行游戏时,每次提示两次,程序不会等待用户的输入。它也会夺去生命(如果它是正确的输入,则为一生,如果不是,则为两个生命),因此无论它采取的是什么都与之前的输入不同。这是我的游戏循环,简化了一下:

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。

10 个答案:

答案 0 :(得分:19)

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

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

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

%*c匹配单个字符,但星号表示该字符不会存储在任何位置。这会消耗enter键生成的换行符,以便下次调用scanf()时,您将以空输入缓冲区开始。

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

答案 1 :(得分:8)

换行。

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

如何解决?

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

char ibuff[2];

while ((scanf("%.1s", ibuff) == 1)
{
    ...
}

答案 2 :(得分:5)

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

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”为“enter”,因此scanf也必须将“输入”键作为字符抓取。

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

enter val: ^[
got: 27
enter val: got: 10

答案 3 :(得分:3)

只是一个猜测,但是您输入的是一个带有scanf的单个字符,但是用户必须输入猜测加上换行符,换行符将作为单独的猜测字符使用。

答案 4 :(得分:3)

scanf(" %c", &fooBar);

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

答案 5 :(得分:2)

吉姆和乔纳森做得对。

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

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

(请注意\n

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

答案 6 :(得分:1)

我注意到几点:

  • scanf("%c")将读取1个字符并将ENTER保留在输入缓冲区中以供下次循环使用
  • 即使从用户读取的字符与i
  • 中的字符不匹配,您也会递增secretWord
  • covered[j]何时成为' - '?

答案 7 :(得分:1)

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

答案 8 :(得分:0)

我在你的代码中看到了一些东西:

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

答案 9 :(得分: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.