【问题标题】:scanf isn't executing on every loop iterationscanf 没有在每次循环迭代时执行
【发布时间】:2016-10-31 10:28:19
【问题描述】:

我正在编写一个有趣的程序(不是为了学校),并且很难弄清楚为什么 scanf 函数没有在我的循环的每次迭代中执行 - 我已经玩弄了两个“for”循环和“while”循环。

我知道这取决于我如何编写 scanf 函数(即 scanf("%s", &variablename); VS scanf("%99[^\n]s", &variablename);)会有所不同,但我已经尝试了所有方法,但我很绝望!

当我对来自scanf 的输入进行printf 检查时,每次迭代只输入一个字符串,因此如果我在第一个输入中输入两个单词,则需要两次迭代来处理- 每一个字。这是我正在描述的代码段:

int main(void){
    int tries = 0;
    int score = 0;
    char question[100];
    char useranswer[100];
    const char *phrase = {"our favorite saying\0"};

    printf("\nQuestion #3 (10 points): What is our secret saying?\n");
        sleep(1);
        tries = 1;

    while (tries<=3){
        printf("YOUR ANSWER:");
        scanf("%s[^\n]", useranswer); 

        if(strncmp(useranswer, phrase, 15) != 0){
            printf ("Nope, try again!\n");
            printf("You have used %d out of 3 tries!\n", tries);
            if (tries == 2){
                printf("Here's your final hint:xxx...\n");
            }
            if (tries == 3){
            printf("You didn't get it. The answer is: our favorite saying!\n");
            }
            tries++;
        }   
        if (strncmp(useranswer, phrase, 15) == 0){
            printf("Damn, you're good.  Well done.\n");
            score += 10;
            break;
        }
    }   

这段代码的输出是:

Question #3 (10 points): What is our secret saying?
YOUR ANSWER:our favorite saying
Nope, try again!
You have used 1 out of 3 tries!
YOUR ANSWER:Nope, try again!
You have used 2 out of 3 tries!
Here's your final hint:xxx...
YOUR ANSWER:Nope, try again!
You have used 3 out of 3 tries!
You didn't get it. The answer is: our favorite saying!

(它只允许我输入一次,然后我输入了“我们最喜欢的说法”。)

【问题讨论】:

  • scanf("%99[^\n]%*c", useranswer);
  • [...] 不是 s 说明符的附加组件...它本身就是一个不同的格式说明符。 %s 读取下一个非空白字符字符串,%[^\n] (not %s[^\n]) 读取除 \n 之外的任何字符的字符串——所以你刚刚读取的代码每个一次一个字(每个循环一个)。
  • "%s[^\n]" 不是有效的scanf() 格式。建议改用fgets(useranser, sizeof useranswer, stdin)
  • @chux 为什么无效?
  • @immibis Ahhh 是的"%s[^\n]" 有效,但肯定没有用,也肯定不是 OP 所希望的。 "%s[^\n]" 会 1a) 扫描空白 - 将它们扔掉,1b) 扫描非空白 - 将结果保存在 useranswer,可能会过度填充,1c) 遇到 EOF 或空白并将其放入回到stdin 2) 扫描并尝试将[ 与前一个空格匹配并失败,结束扫描。很好的收获。

标签: c for-loop printf scanf strcmp


【解决方案1】:

在 cmets 中,您可以找到为什么 scanf 中的格式说明符不起作用。

另一种方法是使用fgets 代替,可能在一个帮助函数中处理读取用户输入时可能出现的一些极端情况:

#include <ctype.h>

char *read_line( char *buf, size_t n, FILE *pfin )
{
    ssize_t length = 0;
    int ch;

    if ( !buf || n == 0 )
        return NULL;
    /*  Consume trailing control characters, like '\0','\n', '\r', '\f'...
        but also '\t'. Note that ' ' is not skipped. */
    while ( (ch = fgetc(pfin)) != EOF  &&  iscntrl(ch) ) { }
    if ( ch == EOF )
        return NULL;
    /*  At least one char is printable  */
    *buf = ch;
    ++length;

    /*  Read from file till a newline or up to n-2 chars. The remaining chars
        are left in the stream buffer. Return NULL if no char is read.      */
    if ( fgets(buf + 1, n - 1, pfin) )
    {
        /*  Trim the string to the first control character                  */
        while ( !iscntrl(buf[length]) ) 
        {
            ++length;
        }
        buf[length] = '\0';       
    }
    return buf;
}

我也会更改以下逻辑。 OP 多次使用strncmp(useranswer, phrase, 15),但那个幻数15 小于phrase 的大小,所以它最终只比较一个子字符串。

while ( tries <= 3 ) {
    printf("YOUR ANSWER:");
    if ( !read_line(useranswer, sizeof useranswer, stdin) ) {
        printf("Error: Unexpected end of input.\n");
        exit(EXIT_FAILURE);
    }
    if( strcmp(useranswer, phrase) == 0 ) {
        printf("Damn, you're good.  Well done.\n");
        score += 10;
        break;
    } else {
        printf ("Nope, try again!\n");
        printf("You have used %d out of 3 tries!\n", tries);
        if (tries == 2) {
            printf("Here's your final hint:xxx...\n");
        }
        if (tries == 3) {
            printf("You didn't get it. The answer is: our favorite saying!\n");
        }
        tries++;
    }
}


最后一点,我发现phrase 的 OP 声明有点奇怪(可能是错字):
const char *phrase = {"our favorite saying\0"};
//            string literals are already ^^ null terminated...

虽然我们可以使用简单的数组声明,例如:

const char phrase[] = "our favorite saying";

还要考虑sizeof phrase 在这两种不同情况下返回的值。


感谢 @chux 提供的所有有价值的提示和有趣的链接:
https://*.com/a/27729970/4944425
https://*.com/a/28462221/4944425
@Dmitri 在他的评论中指出,一旦我们确定两个字符串都以 null 结尾,我们可以使用 strcmp 而不是 strncmp

【讨论】:

  • 考虑*.com/a/28462221/2410359*.com/a/27729970/2410359 以消除潜在的'\n'if ( useranswer[lstr - 1] == '\n') 不安全。
  • @Dmitri 如果用户输入更长的字符串,条件lstr == strlen(phrase) 的第一部分将为假。我关注了 OP 对 strncmp 的使用,但请注意他使用 15 作为幻数。
  • @chux 是的,我没有检查空行,我的错。
  • 好的...但是为什么不直接使用strcmp(),当长度不同时它不会表示匹配?不知道为什么人们会更喜欢strncmp(),除非他只需要匹配字符串的一部分..
  • @dmitri 在这种情况下,两个字符串的格式都很好(据我所知,遵循代码的逻辑),因此没有必要限制比较。正如我所说,我只使用了与 OP 相同的功能,试图指出他的错误(使用固定数字和任意数字),但我同意这没有必要。