How can one flush input stream in C?
Asked Answered
S

8

5

I am not able to flush stdin here, is there a way to flush it? 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:

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)
Steamship answered 5/9, 2009 at 19:20 Comment(4)
FYI, writing a struct to a file is not portable.Site
@Andrew, that depends on your definition of portable. I assume you mean, in this case, there's no guarantee that the structure can be read successfully on an implementation that pads the structure differently. You'd be correct in that. However, portability can mean other things such as "will work on all implementations". As per the standard, writing a structure to a file is perfectly portable, all conforming implementations should be able to do that, and read it back if they don't do something that changes padding (like use #pragma pack, for example).Karilla
Perhaps that would have been better phrased as: FYI, a struct could contain padding in it that could cause issues if you try to read it in another implementation, or even the same implementation, with different padding options.Karilla
Possible duplicate of I am not able to flush stdinReceive
F
8

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.

Fagin answered 5/9, 2009 at 19:35 Comment(1)
fflush() is for standard I/O streams - what do you mean? You don't use it for input files - it is intended to flush buffered output. But that isn't what you said. And it is fine - even sensible - to use fflush(stdout); especially in debugging modes. (It's probably more sensible to use fflush(0), but that is a slightly different issue again.)Crystie
S
10

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.

Site answered 5/9, 2009 at 19:30 Comment(5)
I tried putting \n.But even after entering name,age,salary for the first record,the prompt message for entering Y or N for next record is not getting displayed.Steamship
Adding the '\n' will end up causing scanf to read until it encounters the end of the file (use Control+D in *nix or Control+Z in Windows).Shovelhead
I have posted the updated code,it is not working even after incorporating sscanf and fgets.Steamship
Andrew, added a citation to the standard, hope you don't mind - that's always a good idea to placate the language lawyers. Like me :-)Karilla
Further, for OS's where it is defined, it is likely defined with further limitations, e.g. for example, on Linux, fflush is only valid on seekable streams. Which only applies to stdin if a file is redirected as input (e.g. ./prog <somefile). See: fflush(3) - Linux manual page.Joubert
F
8

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.

Fagin answered 5/9, 2009 at 19:35 Comment(1)
fflush() is for standard I/O streams - what do you mean? You don't use it for input files - it is intended to flush buffered output. But that isn't what you said. And it is fine - even sensible - to use fflush(stdout); especially in debugging modes. (It's probably more sensible to use fflush(0), but that is a slightly different issue again.)Crystie
K
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 );
    }
Knesset answered 5/9, 2009 at 20:42 Comment(1)
Note that you'll have to replace "unsigned int const BUF_SIZE = 1024;" with "#define BUF_SIZE 1024" for this to work in C.Shovelhead
K
2

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 ).

Knesset answered 5/9, 2009 at 19:32 Comment(2)
printf( "\nEnter name,age and basic salary" ); char szTempBuf[1024]; fgets( szTempBuf, 1024, stdin ); sscanf( szTempBuf, "%s %d %f", e.name,&e.age,&e.bs );Knesset
@swatkat, have a look at #4024395Karilla
N
2

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.

Nicholle answered 6/9, 2009 at 1:8 Comment(1)
Thanks, the scanf("%*c"); worked a treat for a tutorial I was trying to follow on Windows. But then I thought, hang on what if I try a getchar() in its place, i.e. before the c = getchar(); I am interested in. And that works as well, which is odd as the top answer to this question (and other similar questions) have the extra getchar(); at the end.Pich
P
1

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));
Papagena answered 16/11, 2017 at 13:7 Comment(0)
S
1
  • 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.

Stercoraceous answered 2/12, 2017 at 8:43 Comment(2)
Was your intent to simply summarize other answers?Muldrow
Revisiting my response, I'm really just emphasizing on error handling. Under no circumstance should you silently discard scanf's return value, as I really illustrated by suggesting to do the same even with strtoul. It's hard to realize for beginners that minding these indicators and that things might be going wrong at some point are worth taking into account from the get-go. Of course, except in the rare cases when the program really isn't supposed to care.Stercoraceous
T
0

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

Thoughtout answered 30/10, 2018 at 11:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.