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)
red0ct
  • 4,840
  • 3
  • 17
  • 44
deovrat singh
  • 1,220
  • 2
  • 17
  • 33
  • 2
    FYI, writing a `struct` to a file is _not_ portable. – Andrew Keeton Sep 05 '09 at 19:44
  • 1
    @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). – paxdiablo Dec 09 '17 at 05:54
  • 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. – paxdiablo Dec 09 '17 at 05:56
  • Possible duplicate of [I am not able to flush stdin](https://stackoverflow.com/questions/2187474/i-am-not-able-to-flush-stdin) – klutt Dec 02 '18 at 03:53

8 Answers8

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.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
Andrew Keeton
  • 22,195
  • 6
  • 45
  • 72
  • 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. – deovrat singh Sep 05 '09 at 19:41
  • 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). – E.M. Sep 05 '09 at 20:09
  • I have posted the updated code,it is not working even after incorporating sscanf and fgets. – deovrat singh Sep 05 '09 at 20:16
  • 1
    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 :-) – paxdiablo Dec 09 '17 at 06:01
  • 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 – David C. Rankin Jul 20 '18 at 06:49
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.

Rob Jones
  • 4,925
  • 3
  • 32
  • 39
  • 2
    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.) – Jonathan Leffler Sep 06 '09 at 00:28
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 );
    }
  • Note that you'll have to replace "`unsigned int const BUF_SIZE = 1024;`" with "`#define BUF_SIZE 1024`" for this to work in C. – E.M. Sep 05 '09 at 20:48
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 ).

swatkat
  • 4,785
  • 1
  • 23
  • 17
  • 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 ); – swatkat Sep 05 '09 at 19:43
  • @swatkat, have a look at https://stackoverflow.com/questions/4023895/how-to-read-string-entered-by-user-in-c/4023921#4023921 – paxdiablo Dec 09 '17 at 07:16
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.

D.Shawley
  • 58,213
  • 10
  • 98
  • 113
  • 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. – atomicules Apr 28 '10 at 11:13
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));
NewBee3
  • 21
  • 3
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.

mar77i
  • 90
  • 6
  • Was your intent to simply summarize other answers? – David Hoelzer Apr 07 '18 at 21:47
  • 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. – mar77i Apr 08 '18 at 22:58
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.

ivan.ukr
  • 2,853
  • 1
  • 23
  • 41