scanf在每个循环迭代中都没有执行

3

我正在编写一个有趣的程序(不是为了学校),但是我很难弄清楚为什么scanf函数在每次迭代循环时都没有执行 - 我尝试过使用'for'循环和'while'循环。

我知道,取决于我如何编写scanf函数(即scanf("%s", &variablename);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!

它只允许我输入一次,我输入了“我们最喜欢的话语”。


5
scanf("%99[^\n]%*c", useranswer); - BLUEPIXY
1
[...] 并不是 s 指示符的附加项,而是一种独立的格式指示符。 %s 读取下一个非空格字符的字符串,而 %[^\n]不是 %s[^\n])则读取除 \n 以外的任何字符的字符串 -- 因此你的代码只能一次读取一个单词(每个循环) 。 - Dmitri
3
"%s[^\n]" 不是 scanf() 的有效格式。建议使用 fgets(useranswer, sizeof useranswer, stdin) 替代。 - chux - Reinstate Monica
int x = scanf("%99[^\n]", useranswer); 这个操作可能会导致 scanf 返回,因此它不能与丢弃操作合并:scanf("%*[^\n]"); getchar();前者读取并丢弃超过 99 个字符到 '\n' 的任何额外字符,后者读取并丢弃换行本身。再次强调,这两个操作不能合并为一个 scanf 调用,因为最左边的操作可能会导致失败,从而意味着其后面的操作不执行。在使用 scanf 时要非常小心!知识就是力量,在这种情况下它来自 manpages... - autistic
жӯӨеӨ–пјҢ继з»ӯдёҠйқўзҡ„дҫӢеӯҗпјҢжӮЁйңҖиҰҒзј–еҶҷдёҖдәӣйҖ»иҫ‘пјҢдҪҝз”Ё x... scanf зҡ„жүӢеҶҢйғЁеҲҶдјҡе‘ҠиҜүжӮЁпјҢеңЁиҝҷз§Қжғ…еҶөдёӢ x еҸҜд»ҘжҳҜ EOFгҖҒ0 жҲ– 1пјҢеҪ“ x дёә 1 ж—¶пјҢжӮЁеҫҲеҸҜиғҪеҸӘжғідҪҝз”Ё useranswerпјҢеӣ дёәе…¶д»–еҖјеҜ№еә”дәҺиҜ»еҸ–й”ҷиҜҜгҖҒж–Ү件结е°ҫе’Ңз©әеӯ—ж®өгҖӮ - autistic
显示剩余5条评论
1个回答

3
在注释中,您可以找到为什么在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),但是这个神奇的数字15phrase的大小要小,因此它最终只比较了一个子字符串。
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++;
    }
}

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

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

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

考虑在这两种情况下, sizeof phrase 返回的值也是不同的。
感谢 @chux 提供的所有有价值的提示和有趣的链接:
https://dev59.com/M3E85IYBdhLWcg3wnU0d#27729970
https://dev59.com/M3E85IYBdhLWcg3wnU0d#28462221
还要感谢 @Dmitri 在他的评论中指出,一旦我们确定两个字符串都是空终止的,我们可以使用 strcmp 而不是 strncmp

@Dmitri 如果用户输入了更长的字符串,条件 lstr == strlen(phrase) 的第一部分将为假。我遵循OP的使用 strncmp 但请注意他使用了 15 作为一个神奇数字。 - Bob__
@chux 是的,我没有检查空行,我的错。 - Bob__
好的...但为什么不直接使用strcmp()呢?毕竟如果长度不同,它也不会表示匹配。除非只需要匹配字符串的一部分,否则我不确定为什么有人会更喜欢strncmp() - Dmitri
@dmitri 在这种情况下,两个字符串都是格式良好的(根据代码逻辑我所知道的),因此不需要限制比较。正如我所说,我只是使用了OP使用的相同函数来指出他的错误(使用固定和任意数字),但我同意这并不必要。 - Bob__
细节:当读取到“空行”时,useranswer [0] 中会有 '\ n',并且 lstr == 1。要使 useranswer [0] 中有 '\0',则必须将读取的第一个文本字符设置为 null 字符。这种罕见事件是黑客攻击的一种手段。 - chux - Reinstate Monica

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接