C 字符串比预期的长,并被视为多个输入

C 字符串比预期的长,并被视为多个输入,c,string,input,protection,C,String,Input,Protection,这是我在这里的第一篇文章,我对C比较陌生(这只是我在大学的第二个单元) 基本上,我正在尝试编写一段代码,询问用户是否希望继续。如果他们写“是”,代码将继续循环。如果他们写“否”,则代码终止程序。如果他们写了其他东西,它只会再次询问,直到有可识别的输入。我正在使用scanf和printf,并试图仅使用它们来创建此代码 char userInput[4]; userInput[0] = '\0'; while ((strcmp(userInput, "yes") != 0) &&

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

基本上,我正在尝试编写一段代码,询问用户是否希望继续。如果他们写“是”,代码将继续循环。如果他们写“否”,则代码终止程序。如果他们写了其他东西,它只会再次询问,直到有可识别的输入。我正在使用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
进行用户输入的陷阱
fgets
或POSIX
getline
是更好的选择,因为它们避免了
scanf
固有的许多问题(“输入缓冲区中还有什么?”)。尽管如此,重要的是要知道如何正确使用
scanf

使用
scanf
只不过是解释(1)实际读取了哪些字符的练习?(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'&&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只是一个会计问题,一旦您知道每个格式说明符的行为,以及输入或匹配失败在失败点停止读取的影响,您就可以回答以下问题:(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");
}
当您使用
scanf(“%3s”,userInput)
时,它将只读取
3个以
'\0'
结尾的
字符到
userInput
缓冲区。但是,如果您键入的字符超过
3个,其余字符仍在输入缓冲区中,等待
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;
}
#包括
#包括
内部主(空)
{
字符用户输入[4];
用户输入[0]='\0';
INTC;
而((strcmp(userInput,“yes”)!=0)和&(strcmp(userInput,“no”)!=0))
{
printf(“是否继续(是/否):”;
scanf(“%3s”,用户输入);
while(1)//耗尽输入
{
c=getchar();
如果(c=='\n')中断;
如果(c==EOF)返回-1;
}
}      
返回0;
}

看起来它几乎是在预期长度之后保存其余的字符,然后直接将它们发送到输入端还是什么

是的,事情就是这样

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

您可以通过操作系统将字符从键盘发送到终端。根据您编写的内容,您的程序将接受文本,直到世界末日(尽管一次三个字符)只要有更多的字符可用,它就会吃掉它们,而且你的按键敲击时间有多远,这并不重要。C++没有办法知道你想让它做任何其他事情。
也就是说,通过调整语义(如其他答案所示)对于这样的呼叫/响应提示,可以获得更直观的行为。

scanf
一次只读取3个字符,其余的字符保留在输入缓冲区中。因此,下次调用
scanf
时,会看到剩余的字符,并立即读取它们。任何事情都不要使用
scanf
。读取整行inp使用
fgets
(或者更好,但不是普遍可用的,
getline
),然后手动解析——在本例中,这意味着去除空白,然后与“是”或“否”进行比较。您可以使用read,这是更好地理解/了解其工作原理的方法”看起来它几乎是在预期长度之后保存其余的字符,并直接将它们发送到输入端或其他什么?”这不是“看起来如此”,这正是正在发生的事情。为什么要“它”“放弃任何输入?如果你的应用程序不需要这些输入,你有责任阅读并丢弃它。我对它还是个新手
#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;
}