使用getchar()清除输入缓冲区时,如何避免按两次Enter键?

时间:2018-07-07 22:59:22

标签: c arrays buffer-overflow getchar

我有这个程序:

#include <stdio.h>
#define SIZE 19

int main(){

char string[SIZE];

while (string[0] != 'A'){
 printf("\nEnter a new string.\n");
 fgets(string,SIZE,stdin);

 int storage = 0;
 while (storage != '\n') 
 {
   storage = getchar(); 
 }
 }
 }

如果输入的字符串超过string []可以容纳的最大字符数,则存在带有getchar()的嵌套while循环。如果不存在,则输入一个包含20个字符的字符串,将导致输出为:

Enter a new string.
12345123451234512345

Enter a new string.

Enter a new string.

问题是,这需要我按Enter两次才能输入新字符串:一次是“ fgets”,一次是嵌套的while循环(这是我对正在发生的事情的理解)。

是否有一种方法可以更改此值,所以我只需要按一次Enter键,或者可以将整个while循环更改为更优雅的方法?

3 个答案:

答案 0 :(得分:1)

如果从fgets接收的缓冲区包含换行符,则说明它读取了输入的所有内容,因此您无需执行其他任何操作。如果不是,则使用额外的循环刷新缓冲区。

答案 1 :(得分:1)

您的想法正确,您只需要仔细考虑如何以及何时需要清空输入缓冲区。

所有面向行的 输入函数(fgets和POSIX getline)将读取并在其填充的缓冲区中包含尾随'\n'({ {1}},只有在缓冲区中有足够的空间时。

使用fgets时,只有两个可能的返回值:(1)指向已填充缓冲区的指针,或者(2)错误时或当文件结束时出现“ fgets 没有读取任何字符。

如果NULL返回一个有效的指针,则由您决定是否读取了完整的输入行,或者输入是否超出缓冲区大小,从而使输入缓冲区中的字符未被读取。 / p>

要进行确定,请检查缓冲区中的最后一个字符是否为fgets ,如果不是,则检查缓冲区中是否包含'\n'个字符,这些字符指示仍保留在字符中。输入缓冲区。您可以通过多种方式做到这一点,可以使用SIZE-1(获取指向strchr的指针),'\n'(获取索引)或使用过时的{{1 }}(以获取字符串的长度),然后检查strcspn处的字符。

(关于首选项的注释,您可以使用任何喜欢的方法,但是在strlenlen-1的任何一种情况下,请保存索引或长度,以便可以用来验证输入是否超出缓冲区的大小,或者用户是否通过生成手动strcspn结束输入。您保存索引或长度以防止必须对其中一个进行重复的函数调用

创建一个简单的 helper-function 清除输入缓冲区,以避免在需要检查的地方放置循环也很有帮助。一个简单的函数就可以解决问题,例如

strlen

如果您更喜欢紧凑但可读性较低的版本,则可以使用单个EOF循环,例如

/* simple function to empty stdin */
void empty_stdin (void)
{
    int c = getchar();

    while (c != '\n' && c != EOF)
        c = getchar();
}

可以将示例的其余部分构造为完成上述每个测试,以提供您所描述的输入处理(尽管使用缓冲区的第一个字符为for来控制循环有点奇怪) ),例如

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

一种可能更有用的方法是,如果没有输入任何内容(例如,仅在空白行上按下 Enter 时,则退出循环)。测试将为'A'。更好的方法是简单地使用#include <stdio.h> #include <string.h> #define STRSIZE 19 /* simple function to empty stdin */ void empty_stdin (void) { int c = getchar(); while (c != '\n' && c != EOF) c = getchar(); } int main (void) { char string[STRSIZE] = ""; /* initialize string all 0 */ while (*string != 'A') { /* up to you, but 'A' is a bit odd */ size_t len = 0; /* variable for strlen return */ printf ("enter a string: "); /* prompt */ if (!fgets (string, STRSIZE, stdin)) { /* validate read */ putchar ('\n'); /* tidy up with POSIX EOF on NULL */ break; } len = strlen (string); /* get length of string */ if (len && string[len-1] == '\n') /* test if last char is '\n' */ string[--len] = 0; /* overwrite with nul-character */ else if (len == STRSIZE - 1) /* test input too long */ empty_stdin(); /* empty input buffer */ } return 0; } 控制输入循环。在那里,您已在进入循环之前验证了读取。您还可以将整个内容包装在while (*string != '\n')循环中,并根据您选择的任何输入条件控制循环退出。

这些都是可以考虑的可能性。仔细研究一下,如果您还有其他问题,请告诉我。

答案 2 :(得分:0)

fgets()确实会读取换行符IF(并且仅当缓冲区足够长才能到达并包含它)以及尾随nul终止符。

您的示例输入为20个字符,其后是换行符,然后是nul终止符。那不会有19个字符的缓冲区。

简单的方法是循环使用fgets(),直到换行符包含在缓冲区中为止。

printf("\nEnter a new string.\n");
do
{
     fgets(string,SIZE,stdin);
       /* 
            handle the input, noting it may or may not include a trailing '\n'
            before the terminating nul
       */
} while (strlen(string) > 0 && string[strlen(string) - 1] != '\n');

此循环将清除输入内容(包括第一个换行符),并且还允许您显式处理(如果需要,则舍弃)所有收到的输入。因此,没有必要对getchar()使用第二个循环。

您没有检查fgets()是否返回NULL,所以也没有检查。建议您检查一下,因为这可能表明输入错误。