【问题标题】:String is longer than expected, and is treated as multiple inputs字符串比预期的要长,并被视为多个输入
【发布时间】:2018-03-13 16:14:03
【问题描述】:

这是我在这里的第一篇文章,我对 C 语言比较陌生(这只是我在 uni 的第二个单元)。

基本上,我正在尝试编写一段代码,询问用户是否希望继续。如果他们写“是”,代码将继续循环。如果他们写“否”,代码将终止程序。如果他们写了其他任何东西,它只会再次询问,直到有一个可识别的输入。我正在使用 scanf 和 printf,并试图仅使用它们来创建此代码。

char userInput[4];
userInput[0] = '\0';

while ((strcmp(userInput, "yes") != 0) && (strcmp(userInput, "no") != 0))
{
    printf("Do you want to continue (yes/no) :");
    scanf("%3s", userInput);
}

为了简单起见,我没有包含其余代码。

例如,我的输入是

xxx

输出是

Do you want to continue (yes/no) :

这很好。但是,如果我输入:

xxxx

输出是:

Do you want to continue (yes/no) :Do you want to continue (yes/no) :

如果我输入

xxxxxxx

我明白了

Do you want to continue (yes/no) :Do you want to continue (yes/no) :Do you want to continue (yes/no) :

似乎它几乎在预期长度之后保存了其余字符,并立即将它们发送到输入或其他什么?我想建立对过长字符串的保护,但我认为这并不理想。

如果问题的结构不好,我很抱歉,任何建设性的批评都值得赞赏。我在任何地方都找不到这个确切的问题,所以我想我会问自己。

【问题讨论】:

  • scanf 一次只读取 3 个字符,其余的留在输入缓冲区中。所以下一次对scanf 的调用会看到剩下的并立即读取它们。
  • 永远不要使用scanf 做任何事情。使用fgets(或者,更好,但不是普遍可用的getline)读取一整行输入,然后手动解析它——在这种情况下,这意味着去除空格,然后与“是”或“比较”不”,就像你一样。
  • 您可以使用 read,这样可以更好地理解/了解它是如何工作的
  • “看起来它几乎将剩余的字符保存在预期的长度之后,并立即将它们发送到输入或其他什么?”这不是“看起来如此”,这正是正在发生的事情。为什么“它”应该丢弃任何输入?如果您的应用程序不需要该输入,则您的有责任读取并丢弃它。

标签: c string input protection


【解决方案1】:

当您使用scanf("%3s", userInput) 时,它只会将3'\0' 结尾的字符读取到userInput 缓冲区。但是,如果您键入的字符数超过 3characters,其余字符仍会出现在输入缓冲区中,等待 scanf 读取。 您可以在每次scanf 之后清空缓冲区以避免这种意外。

#include <stdio.h>
#include <string.h>
int main(void)
{
   char userInput[4];
   userInput[0] = '\0';
   int c;

   while ((strcmp(userInput, "yes") != 0) && (strcmp(userInput, "no") != 0))
   {
        printf("Do you want to continue (yes/no) :");
        scanf("%3s", userInput);

        while(1) // drain the input
        {
            c = getchar ();
            if(c=='\n') break;
            if(c==EOF)  return -1;
        }
    }      
    return 0;
}

【讨论】:

  • 我对 C 还是很陌生,所以我的问题是,getchar (); 是什么?实际上,它是如何工作的?如果我不理解它们,我不喜欢在我的代码中使用它们。我在搜索中发现的唯一其他解决方案是使用 scanf("%*[^\n]");在我也没有得到的原始 scanf 之后立即。
【解决方案2】:

似乎它几乎在预期长度之后保存其余字符,并立即将它们发送到输入或其他什么?

是的,就是这样。

那些字符在那里,坐在输入缓冲区中,等待被消耗,就像前三个字符在某个时间点一样。

您从键盘通过操作系统将字符发送到终端。根据您编写的内容,您的程序将接受文本,直到宇宙尽头(尽管一次三个字符)。只要有更多可用的字符,它就会吃掉它们,而且你的击键时间分布有多远并不重要。 C++ 无法知道您希望它做任何其他事情。

话虽如此,通过调整您的语义(如其他答案所示),可以获得对这样的呼叫/响应提示更直观的行为。

【讨论】:

    【解决方案3】:

    欢迎使用scanf 进行用户输入的陷阱。 fgets 或 POSIX getline 是更好的选择,因为它们避免了 scanf 固有的许多(“输入缓冲区中还剩下什么?”)问题。话虽如此,重要的是要知道如何正确使用scanf

    使用scanf 只不过是一个练习(1)实际读取了哪些字符? (2) 输入缓冲区中有哪些字符未读?为了处理第 2 个问题,您需要一些方法来清空在调用 scanf 之间保留在输入缓冲区中的任何字符,以确保您不会被任何剩余的字符咬伤。您还必须知道至少保留一个字符,否则您将简单地阻止等待输入清除。清空stdin 的标准方法是循环遍历剩余的任何字符,直到您读取结尾的"\n'(用户按“Enter”的结果)或EOF。例如,

    void empty_stdin (void)
    {
        int c = getchar();
        while (c != '\n' && c != EOF)
            c = getchar();
    }
    

    (您也可以将其编写为单个 for 语句,例如 for (int c = getchar(); c != '\n' &amp;&amp; c != EOF; c = getchar()) {},但 while 通常更具可读性。)

    这不仅仅是正确处理输入的问题。由于您想强制用户输入“是”或“否”,因此一般的方法是不断循环,直到满足输入条件。你在正确的轨道上,但没有完全到达那里。相反,您可以这样做:

    char buffer[1024] = "";         /* don't skimp on buffer size */
    
    for (;;) {                      /* loop continually */
        int rtn;                    /* value to hold return from scanf */
        printf ("Do you want to continue (yes/no): ");  /* prompt */
        rtn = scanf ("%1023[^\n]", buffer);             /* read input */
        if (rtn == EOF) {           /* user canceled input, bail */
            fprintf (stderr, "user canceled input.\n");
            exit (EXIT_FAILURE);
        }
        else if (rtn == 1) {        /* value stored in buffer */
            /* check for input meeting criteria */
            if (strcmp (buffer, "yes") == 0 || strcmp (buffer, "no") == 0) {
                empty_stdin();  /* don't leave characters in stdin */
                break;          /* success, break input loop */
            }
        }
        /* handle wrong input */
        empty_stdin();  /* don't leave characters in stdin */
        fprintf (stderr, "error: invalid input.\n\n");
    }
    

    如果您以这种方式进行读取,您可以控制输入缓冲区中剩余的内容,不仅针对循环中的每次读取,而且确保您离开时用户输入的缓冲区中没有字符。

    如上所述,正确使用 scanf 只是一个会计问题,一旦您知道每个 格式说明符 的行为方式,以及 输入 匹配的效果 failures 在失败点停止读取,您可以回答以下问题: (1) 实际读取了哪些字符? (2) 输入缓冲区中还有哪些字符未读?

    一个简短的例子可能会有所帮助:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int main (void) {
    
        char buffer[1024] = "";         /* don't skimp on buffer size */
    
        for (;;) {                      /* process loop - doing stuff */
            printf ("\nprogram processing....\n\n");
            for (;;) {                      /* input - loop continually */
                int rtn;                    /* value to hold return from scanf */
                printf ("Do you want to continue (yes/no): ");  /* prompt */
                rtn = scanf ("%1023[^\n]", buffer);             /* read input */
                if (rtn == EOF) {           /* user canceled input, bail */
                    fprintf (stderr, "user canceled input.\n");
                    exit (EXIT_FAILURE);
                }
                else if (rtn == 1) {        /* value stored in buffer */
                    /* check for input meeting criteria */
                    if (strcmp (buffer, "yes") == 0) {
                        empty_stdin();  /* don't leave characters in stdin */
                        break;          /* success, break input loop */
                    }
                    else if (strcmp (buffer, "no") == 0)
                        goto alldone;
                }
                /* handle wrong input */
                empty_stdin();  /* don't leave characters in stdin */
                fprintf (stderr, "error: invalid input.\n\n");
            }
        }
        alldone:;
    
        printf ("that's all folks...\n");
    }
    

    注意:上方的"yes""no" 响应分别进行比较,以便在"yes"exit 上继续处理"no"

    使用/输出示例

    $ ./bin/scanf_yes_no
    
    program processing....
    
    Do you want to continue (yes/no): what?
    error: invalid input.
    
    Do you want to continue (yes/no): obnoxious long line because cat stepped on kbd
    error: invalid input.
    
    Do you want to continue (yes/no): yes
    
    program processing....
    
    Do you want to continue (yes/no): yes
    
    program processing....
    
    Do you want to continue (yes/no):
    error: invalid input.
    
    Do you want to continue (yes/no): no
    that's all folks...
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多