关于gcc:Scanf在C中循环时跳过彼此

Scanf skips every other while loop in C

我正在尝试开发一个简单的基于文本的子手游戏,并且游戏的主要循环从提示输入每个字母的猜测开始,然后继续检查字母是否在单词中,并使其失去生命 不是。 但是,当我运行游戏时,每次都会出现两次提示,并且程序不会等待用户的输入。 它也可以腾出一条生命(如果是正确的输入,则为一条生命,如果不是正确的输入,则为两条生命),因此它所吸收的一切与先前的输入不同。 这是我的游戏循环,简化了一点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
while (!finished)
{
    printf("Guess the word '%s'
"
,covered);

    scanf("%c", &currentGuess);

    i=0;
    while (i<=wordLength)
    {
        if (i == wordLength)
        {
            --numLives;
            printf("Number of lives: %i
"
, 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!
"
);
            break;
        } else {
            if (covered[j] == '-') {
                break;
            }
        }
        ++j;

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

    }
}

我认为问题出在斯堪夫,以为它没有被服用,但是我不知道为什么。 有人有什么主意吗? 我在Mac OS X 10.5上使用gcc 4.0.1。


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

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

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

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

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


换行符。

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

怎么修?

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

1
2
3
4
5
6
char ibuff[2];

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


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

1
2
3
4
5
6
7
8
9
int main(void) {
    char val;
    while (1) {
        printf("enter val:");
        scanf("%c", &val);
        printf("got: %d
"
, val);
    }
}

输出是:

1
2
3
enter val: g
got: 103
enter val: got: 10

为什么scanf在那里又给您'10'?

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

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

1
2
3
enter val: ^[
got: 27
enter val: got: 10


1
scanf(" %c", &fooBar);

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


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


吉姆和乔纳森说对了。

为了让您的scanf行做您想做的事情(不使用换行符而将其放入缓冲区),我将其更改为

1
2
scanf("%c
"
, &currentGuess);

(请注意
)

尽管对此的错误处理是残酷的。至少您应该将scanf的返回值与1进行比较,如果输入没有返回,则忽略该输入(带有警告)。


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


我注意到了几点:

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

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

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

我宁愿建议您避免使用scanf(),而是使用getchar()scanf()需要大量的存储空间。 getchar()是灯光功能。 因此,您还可以使用-

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

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

  • scanf返回其读取的项目数。您可能要处理返回0或EOF的情况。
  • 我的猜测是用户按字母+ Enter,而您将换行符作为第二个字符。一种简单的检查方法是添加调试printf语句以显示输入的字符。
  • 您的代码将仅匹配匹配字母的第一个匹配项,即,如果单词为" test"且用户输入了" t",则您的代码将仅匹配第一个" t",而不是两个都匹配。您需要调整第一个循环来处理此问题。