【问题标题】:scanf skipped after loop in C [duplicate]scanf在C中的循环后跳过[重复]
【发布时间】:2020-07-01 23:00:17
【问题描述】:
#define len 100

char sourceString[len];
char command;

int main(void)
{
    while (1)
    {   
        printf("\nEnter source: ");
        fgets(sourceString, len, stdin); // this gets skipped on second loop.

        printf("\nEnter command: ");
        scanf(" %c", command) 

        switch (command)
        {
        case 'A':
            printf("%s", sourceString);
            break;

        case 'B':
            printf("filler");
            break;

        default:
            break;
        }
    }

    return 0;
}

无论我使用 fgets 还是 scanf,字符串总是在第二个循环中被跳过。 我尝试在第二个 scanf "%c" 中添加一个空格,但它仍然跳过字符串输入,是的,我尝试读取一行然后读取一个字符。

【问题讨论】:

  • fgets 正在使用换行符,因此您的 scanf 绝对不需要(如果没有害处 - 因为它使用不正确)。甚至不清楚您要在这里做什么。读一行然后读一个字符?还是什么?
  • " 甚至不清楚您要在这里做什么。先读一行然后读一个字符?"是的,这就是我想要做的。
  • 嗯,混合输入法通常是个坏主意。使用fgetc 而不是scanf
  • 对两者都使用fgets(),只需取消引用包含command 的数组即可获取第一个字符——确保读取完整的行。

标签: c while-loop scanf


【解决方案1】:

在 scanf 调用之后插入以下调用

    scanf( "%*[^\n]" );
    scanf( "%*c" );

从输入缓冲区中删除换行符'\n'。

【讨论】:

    【解决方案2】:

    对两者都使用fgets(),只需取消引用包含command 的数组即可获取第一个字符——确保读取完整的行。

    您的问题是scanf()'\n' 留在您的输入流中,在您的下一次迭代中由fgets() 进行(因为fgets() 在输入流中读取并包含下一个'\n') .这使得它看起来像 fgets() 被跳过了——它不仅仅是读取了 scanf() 留下的 '\n'

    #define len 100
    
    int main(void)
    {
        char sourceString[len];    /* don't use global variables */
        char command[len];
    
        while (1)
        {   
            printf ("\nEnter source: ");
            fgets (sourceString, len, stdin); // this no longer skipped in loop.
    
            printf("\nEnter command: ");
            fgets (command, len, stdin); 
    
            switch (*command)
            {
            case 'A':
                printf("%s", sourceString);
                break;
    
            case 'B':
                printf("filler");
                break;
    
            default:
                break;
            }
        }
    
        return 0;
    }
    

    (注意:您必须始终检查每个输入函数调用的返回 - 这是留给您的。请参阅Henry Spencer's 10 Commandments for C Programmers - No. 6 “请注意...”)

    如果您还有其他问题,请告诉我。

    【讨论】:

    • Harry Spencer 的第 3、4 和 9 条诫命已经过时,如今已适得其反。
    • 将不得不在答案中减少这一点,但No. 6 仍然是我的最爱。
    • 我是你不测试潜在的printf 失败:)
    • 同意 -- 这些诫命读起来像是来自很久以前。除非您只是计算字符输出的数量,否则很难想出您想要验证 printf 返回的原因。
    【解决方案3】:

    问题是第二个循环中的 fgets() 函数捕获了 '\n',即当您在第一个循环的 scanf() 中输入输入后按 Enter 键时。 有一个解决这个问题的解决方案是在 scanf() 之后添加一个 getchar() 函数,以便它捕获 '\n' 而不是第二个循环和以下循环的 fgets() 。这是一个示例程序:

    while (1)
    {
        printf("\nEnter source: ");
        fgets(sourceString, len, stdin); 
    
        printf("\nEnter command: ");
        scanf(" %c", &command);
        getchar();
    
        /* Rest of the program would follow */
    
    }
    

    【讨论】:

    • 如果为command 输入了超过1 个字符(例如"Apples",或者手指滑到'A',例如"Aq",则失败)您可以执行for (int c = getchar(); c != '\n' && c != EOF; c = getchar()) {}
    • 还应该测试fgets() null 返回值。
    • @DavidC.Rankin 没错,但如果 scanf() 只需要一个字符,我认为它会正常工作。
    • 是的,这就是问题的全部——scanf() 只需要 1 个字符。因此,如果意外输入了超过 1 个字符,则所有其他字符仍保留在输入缓冲区中未读,只是想在下一次尝试输入时咬你。这就是为什么推荐循环清空输入流直到下一个'\n'
    【解决方案4】:

    以下是解释观察到的行为的步骤:

    • printf("\nEnter source: "); :打印提示。
    • fgets(sourceString, len, stdin);一个输入行被读取,你应该测试返回值以确保fgets()成功。
    • printf("\nEnter command: "); 跳过一行,输出新的提示。
    • scanf(" %c", command) 跳过任何初始空白(包括挂起的换行符)并读取单个字符。但是请注意,用户必须键入 enter 才能使该字符对程序可用,因为终端很可能处于 cooked 模式,即:行缓冲。另请注意,您必须传递 charvariable command 的地址,否则会出现未定义的行为。
    • switch 选择要完成的操作,然后循环跳到下一次迭代
    • printf("\nEnter source: "); :打印提示。
    • fgets(sourceString, len, stdin); fgets() 立即返回空行,因为上面键入的换行符仍在输入缓冲区中挂起。这就是问题所在。

    您可以通过在读取字符后丢弃剩余的待处理行来解决此问题:

    scanf("%*[^\n]");   // read any characters except newline
    scanf("%*1[\n]");   // read at most 1 newline character
    

    您必须对scanf() 使用两次单独的调用,因为如果换行符之前没有字符,第一次转换将失败。

    #incude <stdio.h>
    
    #define LEN 100
    
    int main(void) {
        char sourceString[LEN];
        char command;
    
        for (;;) {
            printf("\nEnter source: ");
            if (!fgets(sourceString, len, stdin))
                break;
    
            printf("\nEnter command: ");
            /* read a single non blank character and discard the rest of the line */
            if (scanf(" %c%*[^\n]", &command) != 1)
                break;
    
            /* discard the pending newline if any */
            scanf("%*1[\n]");  // of just getchar()
    
            switch (command) {
              case 'A':
                printf("%s", sourceString);
                break;
    
              case 'B':
                printf("filler");
                break;
    
              default:
                break;
            }
        }
        return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-10-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-16
      • 2010-12-12
      相关资源
      最近更新 更多