【问题标题】:How can one flush input stream in C?如何在 C 中刷新输入流?
【发布时间】:2010-11-25 22:13:00
【问题描述】:

我无法在此处刷新 stdin,有没有办法刷新它?如果不是,那么如何让getchar() 将一个字符作为用户的输入,而不是scanf() 在输入缓冲区中留下的“\n”??

#include "stdio.h"
#include "stdlib.h"

int main(int argc,char*argv[]) {
    FILE *fp;
    char another='y';
    struct emp {
        char name[40];
        int age;
        float bs;
    };
    struct emp e;
    if(argc!=2) {
        printf("please write 1 target file name\n");
    }
    fp=fopen(argv[1],"wb");
    if(fp==NULL) {
        puts("cannot open file");
        exit(1);
    }
    while(another=='y') {
        printf("\nEnter name,age and basic salary");
        scanf("%s %d %f",e.name,&e.age,&e.bs);
        fwrite(&e,sizeof(e),1,fp);

        printf("Add another record (Y/N)");
        fflush(stdin);
        another=getchar();
    }
    fclose(fp);
    return 0;
}

编辑:更新的代码,仍然无法正常工作

#include "stdio.h"
#include "stdlib.h"

int main(int argc,char*argv[]) {
    FILE *fp;
    char another='y';
    struct emp {
        char name[40];
        int age;
        float bs;
    };
    struct emp e;
    unsigned int const BUF_SIZE = 1024;
    char buf[BUF_SIZE];

    if(argc!=2) {
        printf("please write 1 target file name\n");
    }
    fp=fopen(argv[1],"wb");
    if(fp==NULL) {
        puts("cannot open file");
        exit(1);
    }
    while(another=='y') {
        printf("\nEnter name,age and basic salary : ");
        fgets(buf, BUF_SIZE, stdin);
        sscanf(buf, "%s %d %f", e.name, &e.age, &e.bs);
        fwrite(&e,sizeof(e),1,fp);
        printf("Add another record (Y/N)");
        another=getchar();
    }
    fclose(fp);
    return 0;
}

输出:

dev@dev-laptop:~/Documents/c++_prac/google_int_prac$ ./a.out emp.dat

Enter name,age and basic salary : deovrat 45 23
Add another record (Y/N)y

Enter name,age and basic salary : Add another record (Y/N)y

Enter name,age and basic salary : Add another record (Y/N)

【问题讨论】:

  • 仅供参考,将struct 写入文件是不可可移植的。
  • @Andrew,这取决于您对便携式的定义。我假设您的意思是,在这种情况下,不能保证可以在以不同方式填充结构的实现上成功读取结构。你是对的。但是,可移植性可能意味着其他事情,例如“将适用于所有实现”。根据标准,将结构写入文件是完全可移植的,所有符合要求的实现都应该能够做到这一点,如果他们不做改变填充的事情(比如使用 @ 987654328@,例如)。
  • 也许可以更好地表述为:仅供参考,struct 可能包含填充,如果您尝试在另一个实现中读取它可能会导致问题,或者即使是相同的实现,具有不同的填充选项。

标签: c io stream stdio


【解决方案1】:

fflush(stdin) 是未定义的行为(a)。相反,让scanf“吃”换行符:

scanf("%s %d %f\n", e.name, &e.age, &e.bs);

其他人都认为scanf 是一个糟糕的选择。相反,您应该使用fgetssscanf

const unsigned int BUF_SIZE = 1024;
char buf[BUF_SIZE];
fgets(buf, BUF_SIZE, stdin);
sscanf(buf, "%s %d %f", e.name, &e.age, &e.bs);

(a) 例如,参见C11 7.21.5.2 The fflush function:

int fflush(FILE *stream) - 如果 stream 指向未输入最近操作的输出流或更新流,则 fflush 函数会导致任何未写入的数据该流将被传递到要写入文件的主机环境; 否则,行为未定义。

【讨论】:

  • 我试过放\n。但是即使在第一条记录输入姓名,年龄,工资后,下一条记录输入Y或N的提示信息也没有显示。
  • 添加 '\n' 最终会导致 scanf 读取直到遇到文件末尾(在 *nix 中使用 Control+D 或在 Windows 中使用 Control+Z)。
  • 我已经发布了更新的代码,即使合并了 sscanf 和 fgets 也无法正常工作。
  • Andrew,在标准中添加了引用,希望您不要介意 - 这总是安抚语言律师的好主意。像我一样:-)
  • 此外,对于定义它的操作系统,它可能定义有更多限制,例如例如,在 Linux 上,fflush 仅对 seekable 流有效。如果文件被重定向作为输入(例如./prog <somefile),这仅适用于stdin。请参阅:fflush(3) - Linux manual page
【解决方案2】:

使用 fflush(stdin) 不是一个好习惯,因为它具有未定义的行为。通常,像 scanf() 这样的函数会在标准输入中留下尾随换行符。因此,最好使用比 scanf() 更“干净”的函数。您可以将 scanf() 替换为 fgets() 和 sscanf() 的组合,并且可以取消 fflush(stdin)。

【讨论】:

  • printf("\n输入姓名、年龄和基本工资");字符 szTempBuf[1024]; fgets(szTempBuf, 1024, 标准输入); sscanf( szTempBuf, "%s %d %f", e.name,&e.age,&e.bs );
【解决方案3】:

更新:您需要在循环末尾添加另一个 getchar() 以使用 Y/N 后面的 '\n'。我认为这不是最好的方法,但它会让你的代码按现在的样子工作。

while(another=='y') {
    printf("\nEnter name,age and basic salary : ");
    fgets(buf, BUF_SIZE, stdin);
    sscanf(buf, "%s %d %f", e.name, &e.age, &e.bs);
    fwrite(&e,sizeof(e),1,fp);
    printf("Add another record (Y/N)");
    another=getchar();
    getchar();
}

我建议将您要解析的数据(直到并包括 '\n')读入缓冲区,然后使用 sscanf() 将其解析出来。这样您就可以使用换行符,并且可以对数据执行其他完整性检查。

【讨论】:

  • fflush() 用于标准 I/O 流 - 你是什么意思?您不要将它用于输入文件 - 它旨在刷新缓冲输出。但这不是你说的。使用 fflush(stdout); 很好——甚至是明智的。特别是在调试模式下。 (使用 fflush(0) 可能更明智,但这又是一个稍微不同的问题。)
【解决方案4】:

使用它来代替 getchar():

   char another[BUF_SIZE] = "y";
   while( 'y' == another[0] )
   {
        printf( "\nEnter name,age and basic salary : " );
        fgets( buf, BUF_SIZE, stdin );
        sscanf( buf, "%s %d %f", e.name, &e.age, &e.bs );
        fwrite( &e, sizeof(e) , 1, fp );
        printf( "Add another record (Y/N)" );
        fgets( another, BUF_SIZE, stdin );
    }

【讨论】:

  • 请注意,您必须将“unsigned int const BUF_SIZE = 1024;”替换为“#define BUF_SIZE 1024”才能在 C 中使用。
【解决方案5】:

我会推荐很多其他人建议的fgets()+sscanf() 方法。您也可以在调用getchar() 之前使用scanf("%*c");。这基本上会吃掉一个角色。

【讨论】:

  • 谢谢,scanf("%*c"); 为我尝试在 Windows 上学习的教程提供了帮助。但后来我想,如果我尝试使用getchar() 代替它会怎么样,即之前 我感兴趣的c = getchar();。这也有效,这与最佳答案一样奇怪对于这个问题(和其他类似问题)有额外的 getchar();最后。
【解决方案6】:

如果您在 Windows 下执行此操作,您可以在 getch() 之前使用 winapi 刷新输入缓冲区。

#include <windows.h>
hStdin = GetStdHandle(STD_INPUT_HANDLE);
FlushConsoleInputBuffer(hStdin);

-或-

#include <windows.h>
FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE));

【讨论】:

    【解决方案7】:
    • 正如其他人已经指出的那样,您不应该将结构写入文件。相反,尝试以格式化的方式写入数据。这样,您的文本文件可以通过查找最后一个和倒数第二个分隔符来逐行解析,例如分号。请记住,某些字符(如 '-''.')可能会出现在字符串化浮点字段中。

      int write_data(FILE *fh, struct emp *e) {
          if(fh == NULL || e == NULL)
              return -1;
          fprintf(fh, "%s;%d;%f", e->name, e->age, e->bs);
          return 0;
      }
      
    • 另一件事是每个人都在不断推荐相同的 scanf 系列函数,但没有人检查返回值是否等于要读取的字段数。我认为这是一个坏主意,实际上是在自找麻烦。即使使用strtol/strtod 方式,您也需要进行错误检查:

      int parse_int(char *buf, long *result) {
          if(buf == NULL || result == NULL)
              return -1;
          errno = 0;
          *result = strtoul(buf, NULL, 0);
          if(errno != 0) {
              perror("strtoul");
              return -1;
          }
          return 0;
      }
      
    • 上面的两个代码示例静默返回,如果您打算一直使用现有对象调用它们,这很好;不过,请考虑打印一条错误消息,并在您的文档中说明人们在使用您的函数时应该检查返回值。

    【讨论】:

    • 您是否打算简单地总结其他答案?
    • 重新审视我的回复,我真的只是在强调错误处理。在任何情况下,您都不应该默默地丢弃scanf 的返回值,正如我通过建议即使使用strtoul 也要这样做来说明的那样。对于初学者来说,从一开始就很难意识到关注这些指标以及事情在某些时候可能会出错是值得考虑的。当然,除非在极少数情况下程序真的不应该关心。
    【解决方案8】:

    stdin 不是可刷新的,您只能刷新输出流。 IE。你根本不需要在标准输入上调用flush。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-08-15
      • 2012-12-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-27
      • 1970-01-01
      相关资源
      最近更新 更多