繁体   English   中英

如何在 C 中刷新输入流?

[英]How can one flush input stream in C?

我无法在这里刷新标准输入,有没有办法刷新它? 如果不是,那么如何使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)

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函数会导致该流的任何未写入数据被传送到要写入的主机环境文件; 否则,行为未定义。

更新:您需要在循环末尾添加另一个 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() 将其解析出来。 这样您就可以使用换行符,并且可以对数据执行其他完整性检查。

使用它代替 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 );
    }

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

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

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

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

-要么-

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

     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; }
  • 上面的两个代码示例静默返回,如果您打算一直使用现有对象调用它们,这很好; 不过,请考虑打印一条错误消息,并在您的文档中说明人们在使用您的函数时应该检查返回值。

stdin 不是可刷新的,您只能刷新输出流。 也就是说,您根本不需要在 stdin 上调用 flush。

建议使用以下任何功能来清理标准输入:

1.)

void to_clear_line() {
    scanf("%*[^\n]");  // Reads all "ordinary" characters and discards them except the character '\n'
    scanf("%*c");      // Reads the character '\n' and discards it, cleaning up the stdin
}

2.)

void to_clear_line() {
    char ch;
    while ((ch = getchar()) != '\n' && ch != EOF); // getchar in loop, cleaning up the stdin
}

例如:

while (another == 'y') {
    printf("\nEnter name, age and basic salary: ");
    sscanf(buf, "%s %d %f", e.name, &e.age, &e.bs);
    to_clear_line();
    /* code */
    printf("Add another record (Y/N): ");
    another = getchar();
    to_clear_line();
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM