刷新输入流C的问题

problem with flushing input stream C

我无法在这里刷新stdin,有没有办法刷新stdin?如果没有,那么如何让getchar()从用户输入一个字符作为输入,而不是输入缓冲区中scanf留下的" n"??

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#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
"
);
    }
    fp=fopen(argv[1],"wb");
    if(fp==NULL) {
        puts("cannot open file");
        exit(1);
    }
    while(another=='y') {
        printf("
Enter 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;
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#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
"
);
    }
    fp=fopen(argv[1],"wb");
    if(fp==NULL) {
        puts("cannot open file");
        exit(1);
    }
    while(another=='y') {
        printf("
Enter 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;
}

output for this is :-

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"吃掉"换行符:

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

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

1
2
3
4
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) - If stream points to an output stream or an update stream in which the most recent operation was not input, the fflush function causes any unwritten data for that stream to be delivered to the host environment to be written to the file; otherwise, the behavior is undefined.


更新:您需要在循环结束时添加另一个getchar()以使用Y / N后面的' n'。我不认为这是最好的方法,但它会使你的代码现在正常运行。

1
2
3
4
5
6
7
8
9
10
while(another=='y') {
    printf("
Enter 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():

1
2
3
4
5
6
7
8
9
10
11
   char another[BUF_SIZE] ="y";
   while( 'y' == another[0] )
   {
        printf("
Enter 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 );
    }


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


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


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

    1
    2
    3
    4
    5
    6
    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方式,您也需要进行错误检查:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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;
    }
  • 上面的两个代码示例以静默方式返回,如果您打算一直使用现有对象调用它们,这很好;但是,请考虑打印错误消息,并在文档中说明人们在使用您的函数时应检查返回值。


如果你在windows下这样做,你可以使用winapi在你的getch()之前刷新输入缓冲区。

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

-要么-

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

stdin不是可以刷新的东西,你只能刷新输出流。即你根本不需要在stdin上调用flush。