problem with flushing input stream C

2019-01-15 21:25发布

问题:

I am not able to flush stdin here,is there a way to flush stdin?If not then how to make getchar() to take a character as input from user, instead of a "\n" left by scanf in the input buffer??

#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;
}

EDIT :- updated code ,still not working properly

#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;
}

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)

回答1:

Update: You need to add another getchar() at the end of your loop to consume the '\n' that follows the Y/N. I don't think this is the best way to go, but it will make your code work as it stands now.

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();
}

I would suggest reading the data you want to parse (up to and including the '\n') into a buffer and then parse it out using sscanf(). This way you consume the newline and you can perform other sanity checks on the data.



回答2:

fflush(stdin) is undefined behaviour(a). Instead, make scanf "eat" the newline:

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

Everyone else makes a good point about scanf being a bad choice. Instead, you should use fgets and sscanf:

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) See, for example, 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.



回答3:

Use this instead of 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 );
    }


回答4:

It's not a good practice to use fflush( stdin ) as it has undefined behavior. Generally, functions like scanf() leaves trailing newlines in stdin. So, it is better to use functions that are "cleaner" than scanf(). You can replace your scanf() with a combination of fgets() and sscanf() and you can do away with fflush( stdin ).



回答5:

I would recommend the fgets()+sscanf() approach that a lot of other people have suggested. You could also use scanf("%*c"); before the call to getchar(). That will essentially eat a character.



回答6:

If you are doing this under windows, you can use winapi to flush input buffer before your getch().

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

-or-

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


回答7:

  • As others already pointed out, you should not write a struct to a file. Instead, try to write the data in a formatted manner. This way your text file can be parsed line-by-line by finding the last and second-to-last delimiters, for example semicolons. Keep in mind that certain characters like '-' or '.' may occur in the stringified float field.

    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;
    }
    
  • The other thing is how everybody keeps recommending the same scanf family of functions, but nobody ever checks whether the return value is equal to the number of fields to be read. I think that is a bad idea, effectively asking for trouble. Even with the strtol/strtod way you need error checking:

    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;
    }
    
  • the two code examples above return silently which is fine if you plan to call them using existing objects all the time; consider printing an error message, though, and illustrate in your documentation that people should check the return values when using your functions.



回答8:

stdin is not something flushable, you can flush only output streams. I.e. you don't need to call flush on stdin at all.



标签: c stream