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

问题描述 投票:15回答:10

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

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。

c gcc scanf
10个回答
19
投票

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

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

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

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

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


0
投票

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

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

我宁愿建议你避免使用scanf()而是使用getchar()scanf()re需要大量的内存空间。 getchar()是一个轻功能。所以你也可以用 -

char currentGuess;  
currentGuess=getchar();  
getchar();  // To consume the whitespace character.

8
投票

Newlines.

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

How to fix?

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

char ibuff[2];

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

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

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

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

3
投票

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


3
投票
scanf(" %c", &fooBar);

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


2
投票

吉姆和乔纳森做对了。

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

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

(注意\n

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


1
投票

我注意到几点:

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

1
投票

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


0
投票

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

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