Scanf 在循环中跳过(Hangman)
Posted
技术标签:
【中文标题】Scanf 在循环中跳过(Hangman)【英文标题】:Scanf skipped in loop (Hangman) 【发布时间】:2014-04-12 09:42:01 【问题描述】:这个程序本质上是要求一个秘密字符串,然后要求用户反复猜测该字符串的单个字符,直到他全部猜出来。但是,它每运行一次 while 循环,它就会跳过猜测字符的用户输入。我该如何解决这个问题?
int main()
char guess;
char test2 [50];
char * s = test2;
char output [50];
char * t = output;
printf("Enter the secret string:\n");
fgets(test2, 50, stdin);
for (int i=0;i<49;i++) //fills ouput with _ spaces
*(output +i)='_';
while(strcmp(s,t) != 0)
printf("Enter a guess:");
scanf("%c",&guess);
printf("You entered: %c\n", guess);
showGuess(guess,s, t ); // makes a string "output" with guesses in it
printf("%s\n",t);
printf("Well Done!");
【问题讨论】:
您还将内容与用户输入的'\n'
进行比较。
并且需要输出空终止。
每次输入字母后,您可能想更改output
的内容。否则,您将一遍又一遍地比较相同的事物。
【参考方案1】:
为了快速而肮脏的解决方案,请尝试
// the space in the format string consumes optional spaces, tabs, enters
if (scanf(" %c", &guess) != 1) /* error */;
为了获得更好的解决方案,重做您的代码以使用fgets()
,然后解析输入。
【讨论】:
这些都不起作用。猜测的字符串永远不会与输入字符串匹配。 :( 完全像@User231454的解决方案,这将忽略空格。所以如果输入的字符串有空格,就无法完成游戏。否则,逻辑比我的(当前)解决方案中的逻辑要好:它实际上可以警告玩家输入无效,而不是忽略输入的无效部分。【参考方案2】:正如其他一些答案和 cmets 所指出的,您需要在输入中“使用”“换行符”。
这样做的原因是,从键盘到程序的输入被你的 shell 缓冲,因此,在你真正告诉你的 shell“将其缓冲区的内容传递给程序”之前,程序不会看到任何东西”。此时,程序将能够读取上一个缓冲区中包含的数据,例如您的输入,后跟一个用于在 shell 中验证您的输入的字符:换行符。如果您在执行另一个scanf
之前没有“使用”换行符,那么第二个scanf
将读取换行符,从而导致您目睹的“跳过scanf
”。要消耗输入中的额外字符,最好的方法是读取它们并丢弃您读取的内容(下面的代码做了什么,请注意
while(getc(stdin) != '\n');
scanf
之后的行。这一行的作用是:“当从stdin
读取的字符不是'\n'
时,什么都不做并循环。”)。
作为替代方案,您可以通过 termios(3)
函数告诉您的 shell 不缓冲输入,或者您可以使用任一 curses/ncurses 库作为 I/O。
这就是你想要的:
int main()
char guess;
char test2 [50];
char * s = test2; // 3. Useless
char output [50];
char * t = output; // 3. Useless
int i; // 8. i shall be declared here.
printf("Enter the secret string:\n");
fgets(test2, 50, stdin);
for (i=0;i<50;i++) if (test2[i] == '\n') test2[i] = '\0'; // 4. Remove the newline char and terminate the string where the newline char is.
for (int i=0;i<49;i++) // 5. You should use memset here; 8. You should not declare 'i' here.
*(output +i)='_';
// 1. Either you close the block here, or you don't open one for just one line.
output[49] = '\0'; // 6. You need to terminate your output string.
while(strcmp(s,t) != 0) // 7. That will never work in the current state.
printf("Enter a guess:");
scanf("%c",&guess);
while(getc(stdin) != '\n');
printf("You entered: %c\n", guess);
showGuess(guess,s, t );
printf("%s\n",t);
printf("Well Done!");
return 0; // 2. int main requires that.
您的代码中的其他 cmets:
-
您在
for
循环之后打开了一个块,但从未关闭它。这可能会导致问题。
您将您的 main
声明为一个返回整数的函数...所以您至少应该在末尾添加 return 0;
。
您似乎已经理解char * t = output;
复制output
的值并使用t
作为新副本的名称。这是错误的。您确实在复制某些内容,但您仅在 t
中复制了 output
的地址(也称为引用)。结果output
和t
引用的是同一个数据,如果修改output
,t
会被修改; 反之亦然。否则的话,那些t
和s
变量在当前状态下是没有用的。
您还需要从 test2 缓冲区中的输入中删除换行符。为此,我在fgets
之后添加了一行。
与其“手动”设置数组的所有字节,不如考虑使用memset
函数。
您实际上需要在“填充”后终止输出字符串,因此您应该在最后一个位置分配一个'\0'
。
您将永远无法将 test2 字符串与输出字符串进行比较,因为当您的 test2 在其有意义的内容之后终止时,输出字符串用下划线填充。
虽然循环范围内的变量根据 C99 和 C11 有效,但它们在 ANSI C 中不是标准的;而且通常最好不要在循环中声明任何变量。
另外,“_ 空格”被称为“下划线”;)
这是一个可以满足您的需求的代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LEN 50
int main()
char phrase[LEN];
char guessed[LEN];
char guess;
int i, tries = 0;
puts("Please enter the secret string:");
if(fgets(phrase, LEN, stdin) == NULL)
return 1;
for(i = 0; i < LEN && phrase[i] != '\n'; i++); // Detect the end of input data.
for(; i < LEN; i++) // For the rest of the input data,
phrase[i] = '_'; // fill with underscores (so it can be compared with 'guessed' in the while loop).
phrase[LEN - 1] = '\0'; // NULL terminate 'phrase'
memset(guessed, '_', LEN); // Fill 'guessed' with underscores.
guessed[LEN - 1] = '\0'; // NULL terminate 'guessed'
while(strcmp(phrase, guessed) != 0) // While 'phrase' and 'guessed' differ
puts("Enter a guess (one character only):");
if(scanf("%c", &guess) != 1)
puts("Error while parsing stdin.");
continue;
if(guess == '\n')
puts("Invalid input.");
continue;
while(getc(stdin) != '\n'); // "Eat" the extra remaining characters in the input.
printf("You entered: %c\n", guess);
for(i = 0; i < LEN; i++) // For the total size,
if(phrase[i] == guess) // if guess is found in 'phrase'
guessed[i] = guess; // set the same letters in 'guessed'
printf("Guessed so far: %s\n", guessed);
tries++;
printf("Well played! (%d tries)\n", tries);
return 0;
如果您没有得到什么,请随时在 cmets 中提问。 :)
【讨论】:
你添加的while循环里面有什么?有条件但没有指令可以执行 有一个nop
instruction: ;
。它用于“丢弃”读数,直到满足条件。我已经编辑了我的答案来解释这一点。请注意,如果您想更容易地注意到您在循环中什么都不做,您可以将while
之后的;
替换为
。
不起作用:(。只是继续询问猜测而不显示输出,并且无限这样。
对不起我的错误。它几乎可以工作。它将输出字符串打印为比它长一个 _ 空格,因此字符串永远不会匹配
好的,我要编译你的代码并检查(我通常使用getc
来读取字符,而不是scanf
,所以也许有一个我没有想到的故障,用@ 987654360@,我马上去查)。【参考方案3】:
scanf 正在读取上一次迭代中输入的换行符。您可以使用 getc() 获取 '\n',如下所示:
scanf("%c",&guess);
getc(stdin);
..
这个改变对我有用。尽管@7heo.tk 给出了正确的解释和更简洁的代码
【讨论】:
【参考方案4】:改变
scanf("%c",&guess);
与
scanf(" %c",&guess);
它应该忽略'\n'
。
【讨论】:
对我不起作用。输入所有字母后字符串永远不会匹配 虽然这可以忽略换行符,但它有以下缺点:它还会忽略输入中的所有空格、制表符或其他空白字符(来源:SCANF(3)
);它会循环遍历所有输入字符(即,如果您输入“abc”,它的行为就像您在循环中输入“a”然后“b”然后“c”一样),然后再询问用户另一个输入。以上是关于Scanf 在循环中跳过(Hangman)的主要内容,如果未能解决你的问题,请参考以下文章