How can I clear the input buffer without using rewind function? [duplicate] - c

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)

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.

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.

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

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

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.

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

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.

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

Related

Reading and writing binary files in C

These are 2 separate applications.
In the first one, I tried to store employee details like name, age and salary in the binary file named emp.bin.
In the second application, I tried to view the contents of the file but in place of the name, only the first character appears.
I tried printing each character separately, and it turns out that there's 3 null characters '\n' after each letter in the name that is why it is not printing after the first character.
"Write" application code:
//Receives records from keyboard and writes them to a file in binary mode
#include <stdio.h>
int main()
{
FILE *fp;
char another = 'Y';
struct emp
{
char name[40];
int age;
float bs;
};
struct emp e;
fp = fopen("emp.bin", "wb");
if (fp == NULL)
{
puts("Cannot open the file.");
return 1;
}
while(another == 'Y')
{
printf("Enter the employee name, age and salary: ");
scanf("%S %d %f", e.name, &e.age, &e.bs);
while(getchar() != '\n');
fwrite(&e, sizeof(e), 1, fp);
printf("Add another record? (Y/N)");
another = getchar();
}
fclose(fp);
return 0;
}
"Read" application code:
//Read records from binary file and displays them on VDU
#include <stdio.h>
#include <ctype.h>
int main()
{
FILE *fp;
struct emp
{
char name[40];
int age;
float bs;
} e;
fp = fopen("emp.bin", "rb");
if (fp == NULL)
{
puts("Cannot open the file.");
return 1;
}
while (fread(&e, sizeof(e), 1, fp) == 1)
{
printf("\n%s \t %d \t $%.2f\n", e.name, e.age, e.bs);
}
fclose(fp);
}
Here's the input and output:
How can I correct this code to make it print the whole name?
The problem is in the "writer" application, even before the actual write is performed.
When you get data from the user
scanf("%S %d %f", e.name, &e.age, &e.bs);
you use format %S (capital letter "S". Format specifiers are case sensitive!). As we can read in the printf man page
S
(Not in C99, but in SUSv2.) Synonym for ls. Don't use.
this leads us to %ls format specifier that is described in the following way
s
[...] If an l modifier is present: The const wchar_t * argument is expected to be a pointer to an array of wide characters. Wide characters from the array are converted to multibyte characters
Talking about Windows source we have:
S
Opposite-size character string, up to first white-space character (space, tab or newline). [...]
When used with scanf functions, signifies wide-character array; when used with wscanf functions, signifies single-byte-character array [...]
So, basically, you are reading characters from stdin and converting them to wide chars. In this case every character takes sizeof(wchar_t). Probably in your system this size is 4.
What you need is simply %s format specifier. And since your name array has size 40, I suggest using
scanf("%39s", e.name );
to get the name from user. In this way up to 39 characters will be written, being the 40th reserved to the string terminator '\0'.
As noted by Roberto in his answer, the problem is the %S conversion specifier, which is a typo, you should use %s.
Note however that there are other issues which might pose problems:
you should tell scanf() the maximum number of characters to read for the employee name, otherwise scanf() may write beyond the end of the destination array if input is too long.
if both programs run on separate systems with different endianness, the numbers will be incorrect on the receiving end because their bytes will be stored in the opposite order. For this reason, endianness should be specified and handled explicitly in binary files. Text format tends to be preferred for data transmission.
Here is a modified version:
//Receives records from keyboard and writes them to a file in binary mode
#include <stdio.h>
int main() {
FILE *fp;
char another = 'Y';
struct emp {
char name[40];
int age;
float bs;
} e;
int c;
fp = fopen("emp.bin", "wb");
if (fp == NULL) {
puts("Cannot open the file.");
return 1;
}
while (another == 'Y') {
printf("Enter the employee name, age and salary: ");
if (scanf("%39s %d %f", e.name, &e.age, &e.bs) != 3)
break;
while ((c = getchar()) != EOF && c != '\n')
continue;
if (fwrite(&e, sizeof(e), 1, fp) != 1)
break;
printf("Add another record? (Y/N)");
another = getchar();
}
fclose(fp);
return 0;
}
"Read" application code:
//Read records from binary file and displays them on VDU
#include <stdio.h>
#include <ctype.h>
int main() {
FILE *fp;
struct emp {
char name[40];
int age;
float bs;
} e;
fp = fopen("emp.bin", "rb");
if (fp == NULL) {
puts("Cannot open the file.");
return 1;
}
while (fread(&e, sizeof(e), 1, fp) == 1) {
printf("\n%s \t %d \t $%.2f\n", e.name, e.age, e.bs);
}
fclose(fp);
return 0;
}

C - data not being added to the txt file

I'm supposed to be creating a small client managing program for a private clinic (before you ask, yes this is college work) but I seem to have found 2 bugs that don't allow me to progress on this.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct DATA {
int dia, mes, ano;
};
struct cliente {
char pnome[15];
char snome[15];
char telefone[10];
char bi[10];
float peso, altura;
struct DATA data;
};
int main () {
FILE *c, *tempo;
int op, i;
int stelemovel, sbi;
char filename[15];
struct cliente a;
printf("1 - procurar\n");
printf("2 - inserir novo\n");
printf("3 - gravar\n");
printf("4 - sair\n");
scanf("%d", &op);
switch(op) {
**(...)**
case 2 :
printf("novo cliente. Dados: \n");
tempo = fopen("temp.txt", "a"); //opens a file to store the data on so I can copy it later, this was an attempt to correct a bug I will explain ahead
printf("primeiro nome \n");
scanf("%s", &a.pnome); //scan the first name
fprintf(tempo, "%s", a.pnome); //print to tempo file
printf("segundo nome \n");
scanf("%s", &a.snome); //second name
fprintf(tempo, "%s", a.snome);// print
printf("peso\n"); //you get the picture so far
do{
scanf("%f", &a.peso);
} while (a.peso<1);
fprintf(tempo, "%.2f", a.peso);
printf("altura\n");
do{
scanf("%f", &a.altura);
} while (a.altura<1);
fprintf(tempo, "%.2f", a.altura);
printf("por favor insira data nascimento (dia mes ano)\n");
do {
printf("dia\n");
scanf("%d", &a.data.dia);
} while (a.data.dia<1 || a.data.dia>31);
fprintf(tempo, "%d", a.data.dia);
do {
printf("mes\n");
scanf("%d", &a.data.mes);
} while (a.data.mes<1 || a.data.mes>12);
fprintf(tempo, "%d", a.data.mes);
do {
printf("ano\n");
scanf("%d", &a.data.ano);
} while (a.data.ano<1);
fprintf(tempo, "%d", a.data.ano);
printf("numero telefone\n");
do {
scanf("%s", &a.telefone);
} while (strlen(a.telefone)!=9);
fprintf(tempo, "%d", a.telefone);
printf("numero BI\n");
do {
scanf("%s", &a.bi);
} while (strlen(a.bi)!=9);
fprintf(tempo, "%d", a.bi);
/* printf("%s, %s\n %.2f, %.2f\n %d, %d, %d\n %s, %s\n", a.pnome, a.snome, a.peso, a.altura, a.data.dia, a.data.mes, a.data.ano, a.telefone, a.bi); */
/*this was something I used to test out if the data was saving properly
which is EXCEPT for the a.telefone and the a.bi, they're being printed together for some reason
*/
return main();
case 3 :
printf("nome do ficheiro\n");
scanf("%s", &filename);
c = fopen(filename, "a");
printf("%s, %s\n %.2f, %.2f\n %d, %d, %d\n %s, %s\n", a.pnome, a.snome, a.peso, a.altura, a.data.dia, a.data.mes, a.data.ano, a.telefone, a.bi);
fprintf(c, "%s, %s\n %.2f, %.2f\n %d, %d, %d\n %s, %s\n", a.pnome, a.snome, a.peso, a.altura, a.data.dia, a.data.mes, a.data.ano, a.telefone, a.bi);
/*this part basically should copy all I scanned in case 2 and print it to a document but I can't seem to be able to write on the file. The file is created opened but there's never any content on it */
return main();
**(...)**
return 0;
}
This is the bug I get when printing, a.bi gets printed normally next though which basically tells me the problem is on a.telefone but I can't seem to see it.
I'm writing this here because I'm really out of ideas, slightly saturated too and as you can probably guess I'm not exactly a pro on files. Thanks in advance for any help being provided though.
I'm sure the solution is probably simple but I can't seem to see it at this point...
EDIT
Now if ya guys can help me on why I'm not able to print the stuff I scan to a file this matter can be considered closed.
EDIT 2
Ok seems like it's printing to a file but it's not separating the words through commas.
The reason your code is printing telephone and bi together is because you didn't leave enough space for the '\0' at the end of the string. Strings in C are null terminated. This also indicates your not sanitizing your inputs. Doing scanf("%s", str); is very dangerous and leads to buffer exploits. You should provide a width to it like scanf("%8s", str); or consider using something like fgets().
The telephone string:
char telefone[9];
Is too small to held the phone number you punched in plus the null tereminator of the string. Changing it to something larger as:
char telefone[16];
Will work. But the probllem here is that you don't make any check on the input data for buffer overflow.
Always check for max input.
char telefone[9];
This buffer can hold a string of up to 8 characters, plus terminating '\0' as the 9th element. You are experiencing buffer overflow by writing XXX XXX XXX to it. Enlarge the buffer to at least 10 characters and always check the returned value of scanf in each call:
char telefone[10];
if (scanf("%s", &a.telefone) != 0)
// handle error
Consider using fgets to avoid buffer overflow:
char buf[10];
fgets(buf, sizeof(buf), stdin);
a major problem with the posted code is repeatedly calling fopen() with the same variable on a file that is already open.
Suggest calling fclose() before every new recursion AND before exiting the program.

C read file, struct and infinity loop

I wrote a program that collects user data and saves it to a file. At the moment when he wants to view the file, the program loops and shows only the first record. I do not know what this error is caused.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
FILE *fptr;
struct notification {
char name[50];
char lastname[50];
char price[10];
char descreption[100];
}notification;
void insertRecord()
{
fptr=fopen("G:\\file.txt","a+");
fflush(stdin);
printf("Podaj imie: ");
gets(notification.name);
printf("Podaj nazwisko: ");
gets(notification.lastname);
printf("Podej cene: ");
gets(notification.price);
printf("Podaj opis usterki: ");
gets(notification.descreption);
strcat(notification.descreption,"\n");
if(fwrite(&notification,sizeof(notification),1,fptr) != 1)
{
perror("Blad: ");
} else{
printf("Dane dodane poprawnie\n");
}
fclose(fptr);
}
void readDatabase()
{
struct notification *object2=malloc(sizeof(struct notification));
fptr=fopen("G:\\file.txt","rb");
fread(object2,sizeof(struct notification),1,fptr);
while(!feof(fptr))
{
printf("Imie: %s\n", object2->name);
printf("Nazwisko: %s\n", object2->lastname);
printf("Cena: %s\n", object2->price);
printf("Opis: %s\n", object2->descreption);
printf("==========\n");
}
fclose(fptr);
}
int main() {
int i,option=0,check=0;
do{
printf("1) Dodaj rekord do bazy \n");
printf("2) Odczytaj rekordy z bazy \n");
printf("0) Zakoncz program \n");
scanf("%d", &option);
switch (option)
{
case 1:
insertRecord();
break;
case 2:
readDatabase();
break;
default:
break;
}
}while(check == 0); //petla dziala dopóki zmienna check bedzie równa 0
}
EDIT:
Correct insertRecord function:
void insertRecord()
{
fptr=fopen("G:\\file.txt","a+");
fflush(stdin);
struct notification *obj = malloc(sizeof(struct notification));
printf("Podaj imie: ");
gets(obj->name);
printf("Podaj nazwisko: ");
gets(obj->lastname);
printf("Podej cene: ");
gets(obj->price);
printf("Podaj opis usterki: ");
gets(obj->descreption);
strcat(notification.descreption,"\n");
if(fwrite(obj,sizeof(struct notification),1,fptr) != 1)
{
perror("Blad: ");
} else{
printf("Dane dodane poprawnie\n");
}
free(obj);
fclose(fptr);
}
Now ALL display and insert OK, but in file.txt I see Chinese characters, why?
There are a variety of problems in the readDatabase function
while(!feof)-is-always-wrong
the fread needs to be in the loop.
you don't need to malloc the memory, but if you do malloc memory, you should free it when you're done with it
you always need to check the return value from fopen, because it can and does fail, e.g. because the file is not found
With all that in mind, the readDatabase function should look like this
void readDatabase( void )
{
struct notification object2;
if ( (fptr = fopen("G:\\file.txt","rb")) == NULL )
{
printf( "File not found\n" );
return;
}
while ( fread( &object2, sizeof(struct notification), 1, fptr ) == 1 )
{
printf("Imie: %s\n", object2.name);
printf("Nazwisko: %s\n", object2.lastname);
printf("Cena: %s\n", object2.price);
printf("Opis: %s\n", object2.descreption);
printf("==========\n");
}
fclose(fptr);
}
Move this line:
fread(object2,sizeof(struct notification),1,fptr);
inside your while loop.
scanf("%d", &option); followed by gets() leads to trouble. The first does not consume the '\n' after the number and the second only reads in the short line '\n'.
Do not use scanf(). Do not use gets(). Use fgets(), then parse the input.
scanf() will leave new line character in input stream by default. you can use getchar() function to clear this new line character or you can flush the input buffer like this.
while ((ch = getchar()) != '\n' && ch != EOF);
but don't use fflush(stdin) because if the file stream is for input use, as stdin is, the behaviour is undefined, therefore it is not acceptable to use fflush() for clearing keyboard input. As usual, there are some exceptions, check your compiler's documentation to see if it has a (non-portable) method for flushing input.

File processing in C; won't take input

I'm starting to learn file processing in C. The point of this specific program is to make a file called "clients.dat" where I store the account number, name, and balance of clients at a bank, lets say. I've worked and refined the code so that its a perfect replica of what the textbook provides as an example, yet for some reason mine loops endlessly after the first "scanf" and reprints question marks unto oblivion, without ever making it to the scanf statement inside the while loop. Would anyone have any suggestions as to why this is happening? My compiler is Netbeans and I'm running it on Linux-Ubuntu.
#include <stdio.h>
#include <stdlib.h>
/*
*
*/
int main() {
unsigned int actNumber;
char actName[30];
long double actBalance;
FILE *fPtr;
if((fPtr = fopen("clients.dat", "w")) == NULL) {
printf("File could not be found.\n");
}
else {
printf("Enter the Account Number, Name, and Balance.\n Hit the EoF to exit.\n");
printf("%s","?");
scanf("%d%29s%lf", &actNumber, actName, &actBalance);
while (!feof(stdin)) {
fprintf(fPtr, "%d, %29s, %.2lf\n", actNumber, actName, actBalance);
printf("%s", "?");
scanf("%d%29s%lf", &actNumber, actName, &actBalance);
}
fclose(fPtr);
}
return;
}
The end of file marker is set on stdin only when you press a special key combination on the console.
You can make your loop work correctly by using the return value of scanf(), like this
while (scanf("%d%29s%lf", &actNumber, actName, &actBalance) == 3)
{
fprintf(fPtr, "%d, %29s, %.2lf\n", actNumber, actName, actBalance);
printf("%s", "?");
}
After the first scanf() a '\n' character is left in the input stream, when you call scanf() again inside the loop, the character is consumed then ignored, and scanf() fails returning a value that is less than 3, the process is repeated over and over causing the infinite loop.
The following solution, however, is better. Using fgets() allows a better handiling of the '\n' character left unread in the stdin,
char line[100];
while (fgets(line, sizeof(line), stdin) != NULL)
{
if (sscanf(line, "%d%29s%lf", &actNumber, actName, &actBalance) != 3)
continue;
fprintf(fPtr, "%d;%29s;%.2lf\n", actNumber, actName, actBalance);
fprintf(stdout, "?");
}
note that I remove the white spaces in the printf() format, and replaced the , with ; because in some locales the , is the decimal separator, it's just instintictive not to use it, you can use it if you ensure that . is the decimal separator.

C writing in files problems with repeatable choice

Ok, thats half of my code, but i have problem and i cant fix it. For example i need to pick choice 2 it is adding something to file, i enter[ name, surname, date, gender ] press enter and program shows like menu again(2.Add to file) but this time automatically picks 2 choice and i need to write data another time and it happens all the time when picking choice 2. Please help me find solution of this problem.
#include <stdio.h>
#include <string.h>
#include <conio.h>
#include <windows.h>
#define N 15
struct date
{ int da_year;
int da_month;
int da_day;
};
struct studenti
{
int Nr;
char name[25];
char surname[25];
struct date dzd;
char dzimums;
}students[N] ;
int main()
{
sakums:
// FILE *fails_st;
char line[100];
char *ptk; char * end; int i;int sorted;
int g=0,ch,count=0;
int n;
int choice;
FILE *fails_st = fopen("studenti.txt", "r+");
/* errors ja neizdodas atveert failu */
if (fails_st == NULL)
{
printf("Error opening file!\n");
exit(1);
}
printf("\n2.Add to file");
scanf("%d",&choice);
if(choice==2){
/* write in file */
for (n=0; n<1; n++)
{
printf("%d. Ievadiet: vards, uzvards, datums, dzimums >", n+1);
scanf("%s",&students[n].name);
scanf("%s",&students[n].surname);
scanf("%d.%d.%d", &students[n].dzd.da_day, &students[n].dzd.da_month, &students[n].dzd.da_year);
scanf("%c",&students[n].dzimums);
}
fseek(fails_st, 0, SEEK_END);
for (i=0; i<n; i++)
fprintf(fails_st, " %d. %s %s %d.%d.%d %c\n", N+1, students[i].name,
students[i].surname, students[i].dzd.da_day,
students[i].dzd.da_month, students[i].dzd.da_year,
students[i].dzimums);
fclose(fails_st);
goto sakums;
}
getche();
return 0;
}
Your problem is likely that scanf happily does nothing if the format string that is its first parameter doesn't match the available input. That means it won't change the value of choice, so it will still be 2.
The cause of this is probably that what you input doesn't match your format strings. You can detect when this happens by checking the return value of scanf - it will return the number of variables written to, basically. If that is less than the number of format specifiers in your format string, something went wrong.
At that point, you probably want to consume all the available input (maybe something like int c; do { c = getchar(); } while (c != '\n' && c != EOF); for a simple program like yours) and then prompt the user again.
In particular, I believe your scanf("%c", ...) is likely the culprit: %c, unlike most scanf specifiers, will not ignore leading whitespace, but accept any character. So if you typed in "firstname lastname 1980.6.11 f", for example, the previous scanf call will just have consumed "6.11.1980", leaving " f" in the input buffer (note the space). Then the scanf with %c will read the space into the gender field, and leave the "f" in the input buffer. On the next go around, scanf("%d",&choice); will not do anything because "f" is not a valid number, choice will remain 2 and the "f" will get read as the first name on the next student entry, further confusing matters...
The solution is, I believe, to use scanf(" %c", ...); to explicitly consume leading whitespace.

Resources