C:清除STDIN

时间:2014-09-23 14:49:26

标签: c input buffer stdin fflush

基本上在每个printf之前的windows的代码块中我有“fflush(stdin);”哪个有效。当我将我的代码复制到Linux时,它不起作用,也没有“fflush(stdin);”的替代方案。我发现了。无论我采用哪种方式,输入似乎都没有在缓冲区中清除,或者我的代码中的某些内容不正确。

#include <stdio.h>
#include <math.h>
#include <limits.h>
#include <ctype.h>

int main()
{
   char pbuffer[10], qbuffer[10], kbuffer[10];
   int p=0, q=0, k=0;
   int r, i, Q, count, sum;
   char a[3];
   a[0]='y';
   while(a[0]=='y' || a[0]=='Y')
   {
      printf("Enter a p value: \n");
      fgets(pbuffer, sizeof(pbuffer), stdin);
      p = strtol(pbuffer, (char **)NULL, 10);

      printf("Enter a q value: \n");
      fgets(qbuffer, sizeof(qbuffer), stdin);
      q = strtol(qbuffer, (char **)NULL, 10);

      printf("Enter a k value: \n");
      fgets(kbuffer, sizeof(kbuffer), stdin);
      k = strtol(kbuffer, (char **)NULL, 10);

      while(p<q+1)
      {
         Q=p;
         sum=0;
         count=0;
         while(Q>0)
         {
            count++;
            r = Q%10;
            sum = sum + pow(r,k);
            Q = Q/10;
         }

         if ( p == sum && i>1 && count==k )
         {
            printf("%d\n",p);

         }
         p++;
         a[0]='z';
      }
      while((a[0]!='y') && (a[0]='Y') && (a[0]!='n') && (a[0]!='N'))
      {
         printf("Would you like to run again? (y/n) ");
         fgets(a, sizeof(a), stdin);
      }
   }
   return 0;
}

3 个答案:

答案 0 :(得分:2)

调用fflush(stdin)不是标准的,因此行为未定义(有关详细信息,请参阅this answer)。

不是在fflush上调用stdin,而是调用scanf,传递一个格式字符串,指示函数读取所有内容,包括换行符'\n'字符,像这样:

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

星号告诉scanf忽略结果。

另一个问题是调用scanf将字符读入变量a,格式说明符为" %s":当用户输入非空字符串时,null终止符会创建缓冲区溢出,导致未定义的行为(char a是一个字符的缓冲区;字符串"y"有两个字符 - {'y', '\0'},第二个字符写在缓冲区的末尾。您应该将a更改为包含多个字符的缓冲区,并将该限制传递给scanf

char a[2];
do {
    printf("Would you like to run again? (y/n) \n")
    scanf("%1s", a);
} while(a[0] !='y' && a[0] !='Y' && a[0]!='n' && a[0]!='N' );
}

答案 1 :(得分:0)

不需要刷新输入缓冲区。

OP使用fgets()而不是scanf()进入正确的轨道进行输入,OP应继续使用以下方法:

char a;
while(a !='y' && a !='Y' && a!='n' && a!='N' ) {

  printf("Would you like to run again? (y/n) \n");
  if (fgets(kbuffer, sizeof(kbuffer), stdin) == NULL) 
    Handle_EOForIOerror();

  int cnt = sscanf(kbuffer, " %c", &a); // Use %c, not %s
  if (cnt == 0) 
    continue; // Only white-space entered
  }

最好不要使用scanf(),因为它试图一次性处理用户IO和解析,并且做得不好。


某些现有OP的问题源于fgets()之后的scanf(" %s", &a);(UB应该是scanf(" %c", &a);。将scanf()fgets()混合通常会出现问题scanf(" %c", &a);在输入缓冲区中留下 Enter '\n',迫使代码在下一个fgets()之前想要输入缓冲区。除此之外fgets() 1}}获取陈旧的'\n'而不是新的信息行。

使用fgets()用户IO,需要否定刷新。


示例fgets()包装

char *prompt_fgets(const char *prompt, char dest, long size) {
  fputs(prompt, stdout);
  char *retval = fgets(dest, size, stdin);
  if (retval != NULL) {
    size_t len = strlen(dest);
    if (len > 1 && dest[len-1] == '\n') { // Consume trailing \n
      dest[--len] = '\0';
    }
    else if (len + 1 == dest) {  // Consume extra char
      int ch;
      do {
        ch == fgetc(stdin);
      } while (ch != '\n' && ch != EOF);
    }
  return retval;
  }  

答案 2 :(得分:0)

我认为你要做的事情比看起来更难。

我对你要做的事情的解释是禁用提前输入类型,这样如果用户在程序处理其他内容时输入某些字符,则它们不会出现在提示符处。这实际上很难做到,因为它是一个操作系统级功能。

在打印提示之前,您可以在设备上执行非阻塞读取,直到您在errno中获得EWOULDBLOCK。或者tcsetattr function family可能有所帮助。看起来有一种方法可以在那里消耗文件描述符的输入,但它可能与fgets / fscanf的交互很糟糕

更好的想法是不要担心它。 Unix用户习惯于预先输入类型,你想要的是他们想要的意外行为。