I am having a problem with the Enter key or character in the stdin stream messing up following input calls.
let's say I have one input call, so I enter in the stuff.
but then takes the Enter key as input for the next input call.
I think in c++ there is cin.ignore() to do the trick.
I just can't find the C version.
The input methods are getchar() and gets().
Sorry if this is a duplicate. I couldn't find the question that matches mine.
thanks for any help!
printf("Do you want to view the lines? ");
int choice = getchar();
while (choice == 'y')
{
char line[80];
printf("What line do you want to see? ");
gets(line);
if (line != "all")
{
n = atoi(line);
printf("Line %d: %s\n",n,list[n]);
}
else
for (int i = 0; i<size; i++)
printf("%s \n",list[i]);
printf("Any more lines? ");
choice = getchar();
}
I admit that this is extremely basic, but still learning .
You simply need to keep calling getchar to consume the characters you don't want from the stream. If you know there's always a single additional character then it is as simple as making one additional call to getchar.
If you want to remove multiple characters from the stream or deal with situations where the input may actually contain something you really need you can do something like the code below instead of your choice = getchar().
do
{
choice = getchar();
} while(choice=='\n'); // Add any other characters you may want to skip
This will keep removing characters (in this case only if they are newline) but leave choice set to the first non-removed character.
By the way, you can't compare strings like that (!= "all") use the C string compare functions, e.g. strcmp().
More generically the C++ istream ignore can be roughly written in C as something like the code below; call with stdin to skip characters from the standard input:
int ignore(FILE *stream, int n, int delim)
{
int c;
while(n>0)
{
c = getc(stream);
if(c==EOF || c==delim)
{
return c;
}
n--;
}
return(c);
}
Related
I have to finnish a college project, and a part of my code is acting strangely.
The goal of that part is to get an user input of an integer and store it in a variable so that i can use it later, however if the user inputs a character I have to ask for the number again.
I used the scanf function to get the user input and put it inside a while loop to continuously ask for the input in case it's invalid.
The problem is that when a user inputs a character, the code freaks out and starts running the while loop without stopping in the scanf to get the user input.
It makes sense that the loop condition is always true but the strange part is that it doesn't stop to read new inputs.
I deconstructed my code in order to replicate the problem to make it easier to debug.
I know that there are some useless variables but in my original code they are useful, I just kept them there to make it look similar to the original.
I can only use scanf to get user input, despite knowing them, in this project I am only allowed to use scanf. I can't use scanf's format to get characters, only numerical types are allowed in this project.
C11 is the version of the standart we are using in classes.
I'm sory if the solution for this is a dumb thing, I'm not good at C and I'm having some difficultlies this semester...
Thanks in advance.
while (!verification) {
printf(">>>"); //write values in here
check = scanf("\n%d", &var); //input a number and store the number of valid inputs
if (check) verification = 1; //if the input is a number then the while condition should turn to false with this statement
printf("var = %d, check = %d, verification = %d\n", var, check, verification); //printing all variables
}
If the user does not input an integer there are characters left in the input stream after the call to scanf. Therefor you need to read to end of line before making the next attempt to read an integer. Otherwise scanf will try to read the same non-integer characters again and again. Here is an example:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int ch, i, n;
n = scanf("%d", &i);
while (n == 0) {
fprintf(stderr, "wrong input, integer expected\n");
do {
ch = getchar();
} while ((ch != EOF) && (ch != '\n'));
n = scanf("%d", &i);
}
if (n == 1) {
printf("%d\n", i);
} else { /*n == EOF*/
fprintf(stderr, "reading input failed\n");
exit(EXIT_FAILURE);
}
return 0;
}
Don't use scanf() to read input from the user.
It's really only meant for reading data that's known to be in a particular format, and input from a user... often isn't.
While you do correctly check the return value of scanf("%d"), and could fix the case where the input isn't a number, you'll still have problems if the input is either an empty line, or a number followed by something else (123 foobar).
In the case of an empty line scanf() will continue waiting for non-whitespace characters. This is probably confusing, since users will expect hitting enter to do something.
In the case there's trailing stuff after the number, that stuff stays in the input buffer, and the next time you read something, it gets read. This is again probably confusing, since users seldom expect their input to one question to also act as input to another.
Instead, read a full line with fgets() or getline(), then run sscanf() or strtol() on that. This is much more intuitive, and avoids the disconnect caused by scanf() consuming input lines only partially (or consuming more than one line). See also e.g. scanf() leaves the new line char in the buffer
Here, using getline() (POSIX, even if not in standard C. Use fgets() instead if getline() is not available):
#include <stdio.h>
int main(void)
{
char *line = NULL;
size_t len = 0;
int result;
printf("Please enter a number: ");
while (1) {
if (getline(&line, &len, stdin) == -1) {
/* eof or error, do whatever is sensible in your case */
return 1;
}
if (sscanf(line, "%d", &result) != 1) {
printf("That didn't seem like number, please try again: ");
continue;
}
break;
}
printf("You entered the number %d\n", result);
}
The problem is you must discard offending input when the conversion fails.
Here is a simple solution using only scanf() as instructed:
#include <stdio.h>
int main() {
int n;
for (;;) {
printf("Enter an number: ");
switch (scanf("%d", &n)) {
case 1:
/* successful conversion */
printf("The number is %d\n", n);
return 0;
case 0:
/* conversion failure: discard the rest of the line */
scanf("*[^\n]"); // discard characters before the newline if any
scanf("*1[\n]"); // optional: discard the newline if present
printf("Invalid input. Try again\n");
continue;
case EOF:
/* input failure */
printf("Premature end of file\n");
return 1;
}
}
}
I am trying to get a single digit number from stdin.
Using scanf("%d",&choice); is not good because if something like 3fjios or fjaifdj is entered then it keeps everything after the digit (if there is one), so if later I have scanf("%s",name); it takes the other chars and messing up. And also using scanf is bad (or so it seems from Google).
After a lot of digging I understand that we should use fgets, to read input into a string and then parse through it.
But! Nowhere is explained how to properly clear the buffer afterwards.
So if I do something like:
char choice[3];
do {
fgets(choice, 3, stdin);
scanf("%*[^\n]");
scanf("%*c");//clear upto newline
} while (choice[1] != '\n');
this works only if I enter a string longer than 2 chars.
When I enter a single char for fgets then the scanf actually waits for another input... which is bad.
The other big problem is if I enter more than 2 chars (a digit and '\n') then the first 2 chars go into choice, the rest are stuck in the buffer. All the approaches to clearing it seems like they require one to build a nuclear power plant first...
Also, what happens if the user enters an infinitely (a really long) long string?
Can you please show a simple way that will allow the user to enter some string of some (unknown) length, and then to properly check if it contains exactly a single digit at the start, followed by '\n'?
Any other input should loop back to get a new input from the user again.
please don't use complex solutions, only standard simple C please.
I can't believe I wasted 6 hours on this supposedly simple technical thing, just getting an input from the user. Solving the actual problem was easier...
Do not use scanf. It is making things overly complicated. Just use getchar to read and discard the line. eg:
int read_input(void) {
int n;
n = getchar();
if( getchar() == '\n' || n == EOF)
return n;
else
do n = getchar(); while ( n != '\n' && n != EOF);
fputs("invalid entry: ", stderr);
return read_input();
}
int main(void) {
int input;
input = read_input();
printf("user entered: %c\n", input);
return EXIT_SUCCESS;
}
I have a trouble with scanf and a manual function to get string in the input.
Here is my manual function to get a line of string in input (I also get the [nl] character):
void getln(char *a) {
int i,c;
i=0;
do {
c=getchar();
a[i]=(char)c;
i++;
} while(c!='\n');
}
Then, I using it like this (char hs.school[40]; char hs.pc[20]; int hs.age;):
printf("Import age: ");
scanf("%d",&hs.age);
printf("Import personal code: ");
getln(hs.pc);
printf("Import school: ");
getln(hs.school);
The output:
Import age: 18
Import personal code: Import school: Vo Thi Sau
Why the getln call right after scanf call is ignored? (But the next getln works well)
Can you explain me the details and suggest me how to fix this bug. Thanks!
Edited:
Here is my full code that take the user inputs and export that inputs back to the screen, which is run well after I did a little trick, but I decide to make a question, mainly for expanding my knowlegde ^_^ Thanks for your answers.
#include<stdio.h>
void getln(char *);
void putstr(char *);
int main(void) {
struct Student {
struct Fullname {
char first[10],middle[20],last[10];
}fu;
struct Native {
char social[30],district[30],province[30];
}na;
struct Score {
double maths,physics,chemistry;
}sc;
char pc[20],school[40];
int age;
}hs;
printf("Import stage:\n");
printf("- Import full name:\n");
printf("++ First name: ");
getln(hs.fu.first);
printf("++ Middle name: ");
getln(hs.fu.middle);
printf("++ Last name: ");
getln(hs.fu.last);
printf("- Import native living place:\n");
printf("++ Social: ");
getln(hs.na.social);
printf("++ District: ");
getln(hs.na.district);
printf("++ Province: ");
getln(hs.na.province);
printf("- Import school: ");
getln(hs.school);
printf("- Import personal code: "); // I have done a little trick
getln(hs.pc); // before I post the question,
printf("- Import age: "); // which swaped these two stage,
scanf("%d",&hs.age); // but it's works like a charm ^_^
printf("- Import scores:\n");
printf("++ Mathematics: ");
scanf("%lf",&hs.sc.maths);
printf("++ Physics: ");
scanf("%lf",&hs.sc.physics);
printf("++ Chemistry: ");
scanf("%lf",&hs.sc.chemistry);
printf("\nExport stage:\n");
printf("- Full name: ");
putstr(hs.fu.first);
printf(" ");
putstr(hs.fu.middle);
printf(" ");
putstr(hs.fu.last);
printf(".\n");
printf("- Native living place: ");
putstr(hs.na.social);
printf(", ");
putstr(hs.na.district);
printf(", ");
putstr(hs.na.province);
printf(".\n");
printf("- School: ");
putstr(hs.school);
printf(".\n");
printf("- Personal code: ");
putstr(hs.pc);
printf(".\n");
printf("- Age: %d.\n",hs.age);
printf("- Scores (Mathematics, Physics, Chemistry): %.2lf, %.2lf, %.2lf.\n",hs.sc.maths,hs.sc.physics,hs.sc.chemistry);
return 0;
}
void getln(char *a) {
int i,c;
i=0;
do {
c=getchar();
a[i]=(char)c;
i++;
} while(c!='\n');
}
void putstr(char *a) {
int i;
i=0;
while(a[i]!='\n') {
putchar(a[i]);
i++;
}
}
After taking input hs.age you pressed in Enter, which is a \n character. So your getln() is called but the loop is broken just after one iteration as c contains '\n'. if you print hs.pc, there will be a new line in your output screen.
You are not clearing the input buffer. So in this newline will be placed after the first input given to scanf. So getchar will get the new line as a input. So loop will quit.
Use this line after the scanf.
int c;
if ( scanf("%d",&hs.age) != 1 ) {
printf("Invalid Input\n");retrun 0; }
while((c=getchar()) != '\n' && c != EOF );
It will clear the input buffer. Then it will ask the second input from the user.
Your getln call isn't skipped, it is taking the newline character left in stdin (the input buffer) as its input and it reads '\n' as c, assigns it to a[i], checks whether c is a '\n' char and exits.
To solve the initial problem, you need to clear the input buffer before calling getln. You can either do that with the while loop as suggested in the other answer, or you can craft a proper format string for scanf that will consume the newline, emptying the buffer. (not foolproof), but an alternative scanf would be:
scanf(" %d%*c",&hs.age);
Which would skip all whitespace before the number (including any newlines), read the decimal value, and then read and discard the newline. Note: this only works for a number without trailing characters. Entering 13abc would leave bc\n in stdin. The while loop in this case is more flexible as it reads all characters until a newline is encountered and is probably the better choice:
scanf(" %d",&hs.age);
while ((c = getchar()) != '\n' && c != EOF);
As for your getln function, it only needs to read each character into a[i]. There isn't a real need for c. You will also want the same checks on your input so that no newline is left. You will also want to check i against the maximum lengths of a minus 1. I would suggest a #define MAXS 128 for the maximum string length for your input. That would allow something to test i against to prevent writing beyond the end of your string.
Here is an alternative to your getln. Note: it is type int allowing it to return the length of the line read so you can determine what to do if it has reached MAXS (as there will still be characters in stdin at that point). As a general rule, if you are doing something in a function when there is a potential for error, it is better to return a value indicating success/failure/problem:
#define MAXS 128
...
int getln (char *a)
{
size_t i = 0;
while ((a[i] = getchar()) != '\n' && a[i] != EOF)
{
i++;
if (i == MAXS - 1)
{
a[i] = 0;
break;
}
}
return i;
}
Can you explain me the details ...
I'll try not to use confusing terms such as buffer.
You probably already know that "%d" corresponds to a set of decimal digit characters which get transformed into an int. When you press 'Enter' as others have suggested, the '\n' character is transmitted via stdin. '\n' isn't a decimal digit character, so it gets placed back onto the stream for your getln function to discover later on...
In reality, your "getln call right after scanf call" probably isn't ignored; it's probably just reading the trailing '\n' and seeing an empty line.
That is assuming the other problem isn't coming into play. getln can't see how many bytes a points to, so it can't tell when it's about to overflow, and hence makes no attempt to prevent buffer overflows... You've basically rewritten gets. If your input is lengthy enough, then I suppose this could also cause your problem... A buffer overflow is undefined behaviour, and the consequences of using undefined behaviour are undefined.
On the topic of undefined behaviour, since getln isn't technically producing a string, I do hope you're not using it as input for a standard string function later on...
Also on the topic of undefined behaviour, what do you suppose might happen if the user enters something that isn't a set of decimal digits? scanf conveys input errors via the return value... so never ignore the return value. You can (and should, at some point) find more information about this in the scanf manual.
... and suggest me how to fix this bug.
It doesn't make a whole lot of sense to discard user input, but unfortunately you can't expect a solution that results in better user experience without blowing your code size (and this explanation) well out of proportion.
You can discard the remainder of the line (which is probably just a '\n') following the set of decimal digits using scanf like so: scanf("%*[^\n]"); getchar();... Following the "%d" scanf call, of course... You could even merge the two together, like so:
if (scanf("%d%*[^\n]", &hs.age) != 1) {
puts("ERROR: EOF or file access error.");
exit(0);
}
getchar();
Unfortunately, if your user uses the spacebar key rather than enter, he or she probably won't find out about the problems with this until it's slightly too late...
As for the buffer overflow problem, I recommend using fgets rather than gets. fgets also has failure modes, which are conveyed via the return value and the contents of the array. The return value is used to convey EOF and file access errors, and the presense (or rather lack of) of a '\n' in the return value is used to convey when the input line was too large to store in the array. We can notify the user of the overflow (which I'm sure they'll appreciate) and discard the excess using the same scanf trick used earlier...
if (fgets(hs.pc, sizeof hs.pc, stdin) == NULL) {
puts("ERROR: EOF or file access error.");
exit(0);
}
size_t size = strcspn(hs.pc, "\n");
if (hs.pc[size] != '\n') {
printf("WARNING: MAXIMUM SIZE OF %zu EXCEEDED! LINE TRUNCATED.\n", sizeof hs.pc - 1);
scanf("%*[^\n]");
getchar();
}
hs.pc[size] = '\0';
I suppose it would make sense to wrap these solutions into functions, except that the functions would then promote the discarding of user input. Nonetheless, the later one is lengthy enough that you'd most likely benefit from abstraction...
void getln(char *a, size_t a_size) {
if (fgets(a, a_size, stdin) == NULL) {
puts("ERROR: EOF or file access error.");
exit(0);
}
size_t size = strcspn(a, "\n");
if (a[size] != '\n') {
printf("WARNING: MAXIMUM SIZE OF %zu EXCEEDED! LINE TRUNCATED.\n", a_size - 1);
scanf("%*[^\n]");
getchar();
}
a[size] = '\0';
}
... and now you can use that like so: getln(hs.pc, sizeof hs.pc);
What I am trying to accomplish is prompting the user with the question of do they want to run the program again. They either type y or n. If y, it reruns the program. If no, it stops the program. Anything other than those two will prompt an error and ask the question again. I'm used to C# where strings are not complicated, but in C, I guess there technically isn't strings, so we have to use either char arrays or char pointers. I've tried both, none that work that way I want, but I'm probably the problem. This is what I have.
char answer[1] = "a";
while (strcmp(answer, "y") != 0 || strcmp(answer, "n") != 0)
{
printf ("\n\nWould you like to run the program again? Type y or n. Then, hit Enter.");
scanf ("%c", answer);
if (strcmp(answer, "y") == 0)
{
main();
}
else if (strcmp(answer, "n") == 0)
{
continue;
}
else
{
printf ("\nERROR: Invalid input was provided. Your answer must be either y or n. Hit Enter to continue.");
F = getchar();
while ((getchar()) != F && EOF != '\n');
}
}
I have other while loops similar to this that work as expected, but use a float. So I'm assuming the problem is me using char here. What happens right now is that it doesn't even prompt the user for the question. It just asks the question and shows the error right afterwards. I'm sure there are other things wrong with this code, but since I can't get the prompt to work, I cannot test the rest of it yet.
I suggest using a light weight getchar() instead of the heavy scanf.
#include <stdio.h>
int c; /* Note getchar returns int because it must handle EOF as well. */
for (;;) {
printf ("Enter y or n\n");
c = getchar();
switch (c) {
case 'y': ...
break;
case 'n': ...
break:
case EOF:
exit(0);
}
}
"a" is a string literal == char id[2]={'a','\0'} //Strings are
char arrays terminated by zero, in C
'a' is a char literal
strcmp is just "compare each char in two strings, until you hit '\0'"
scanf ("%c", ___); expect an address to write to as the second
argument. Functions in C cannot modify their arguments (they don't
have access to them--they get their own local copy) unless they have
a memory address. You need to put &answer in there.
Jens has already basically answered the question, you most likely want to use getchar so that you can detect EOF easily. Unlike scanf("%c",...), getchar will not skip spaces, and I believe both versions will leave you with the unprocessed rest of the input line (a newline character ('\n') at least) after each getchar. You might want to something like
int dump;
while((dump=getchar())!='\n' && dump!=EOF) {};
So that you discard the rest of the line once you've read your first character of it.
Otherwise, the next getchar will get the next unprocessed character of the same line. ('\n' if the line was a single letter).
Here is one way to do it. It is by no means the only way to do it, but I think it accomplishes what you want. You should not call the main function recursively.
#include <stdio.h>
#include <stdlib.h>
void run_program()
{
printf("program was run.");
}
int main() {
char answer[2] = "y\0";
int dump;
do {
if (answer[0] == 'y')
{
run_program(); /* Not main, don't call main recursively. */
}
printf ("\n\nWould you like to run the program again? Type y or n. Then, hit Enter.\n");
scanf ("%1s", answer);
/* Dump all other characters on the input buffer to
prevent continuous reading old characters if a user
types more than one, as suggested by ThorX89. */
while((dump=getchar())!='\n' && dump!=EOF);
if (answer[0] != 'n' && answer[0] != 'y')
{
printf ("Please enter either y or n\n");
}
} while (answer[0] != 'n');
return 0;
}
Using %s instead of %c, reads in the new line so that the new line character is not in the stdin buffer which would become answer then next time scanf was called.
The run_program function is just a function where you would put your program's logic. You can call it whatever you want. I did this to separate out the menu logic from the logic of the actual program.
Well, you are comparing two strings instead of characters.
If you want to compare two character you have to follow this syntax:
char c;
scanf("%c",&c);
if(c == 'y')
//do something
else
//do nothing
Here is how the code's written.
int main()
{
char enteredName[30];
char stringNum[4];
char continueLetter = 0;
int continueProgram = 0;
int enteredAge;
int i;
do
{
memset(enteredName,'\0', 30);
printf("Please enter a name: ");
fgets(enteredName, 29, stdin);
printf("\n\nNow please enter your age: ");
fgets(stringNum, 3, stdin );
for(i = 0; i < 30; i++)
{
if (enteredName[i] == '\n')
{
enteredName[i] = '\0';
break;
}
}
for(i = 0; i < 4; i++)
{
if (stringNum[i] == '\n')
{
stringNum[i] = '\0';
break;
}
}
enteredAge = atol(stringNum);
} while();
When I run through the loop a second time, I'm not able to enter a new name into the char array, it just goes to the next prompt (the age). Unless this issue involves linked lists, the problem seems to be with something else. Could you help me find the error? Thanks!
Your second fgets call leaves characters (specifically the newline) waiting to be read from stdin if you enter a two digit age.
Increase the length parameter to match the array size:
fgets(stringNum, 4, stdin);
Or better:
fgets(stringNum, sizeof stringNum, stdin);
You probably want to do the same for enteredName.
From the fgets(3) man page:
The fgets() function reads at most one less than the number of characters
specified by size from the given stream and stores them in the string
str.
You don't need to reserve the extra array entry for the null-terminator like you're doing - fgets will handle that correctly on its own.
The problem is,you are not flushing the input buffer that is why the fgets() takes you directly to the second prompt asking age.This is common problem encountered,just add fflush(stdin);//my compiler supports itafter fgets();.Here is the code which has worked for me hope it works for you too :
EDIT: There is one very useful post providing information regarding fflush().As it is described that fflush is basically meant to be called to an output stream.Although some compilers provide support for flushing stdin,this is considered an undefined behavior.While having another look at the program, I found out that using sizeof can work wonders and is valid, So, I have modified the program for better. The use of sizeof is also described in one of the answers here.
#include<stdio.h>
#include<stdlib.h>
int main()
{
char enteredName[30];
char stringNum[4];
int continueProgram=0;
int i;
while(continueProgram<3)
{
setbuf(stdout,NULL);
printf("Please enter a name: ");
fgets(enteredName, sizeof enteredName, stdin);
printf("\n\nNow please enter your age: ");
fgets(stringNum,sizeof stringNum, stdin );
for(i = 0; i < 30; i++)
{
if (enteredName[i] == '\n')
{
enteredName[i] = '\0';
break;
}
}
for(i = 0; i < 4; i++)
{
if (stringNum[i] == '\n')
{
stringNum[i] = '\0';
break;
}
}
//enteredAge = atol(stringNum);
continueProgram++;
}
return 0;
}
The problem is that you don't know whether the string that has been read contains a newline or not. If it doesn't contain a newline, then this is going to be read by the next call to fgets, leaving an empty string in it. To prevent it, check if the line contains a newline character at the end. If not just read it using getchar() and Voila!!(Note that this solution is valid only to your problem, not in general). This code is to be added after reading the stringNum string.
if(stringNum[strlen(stringNum)-1]!='\n')
{
getchar();
}
This was happening because, if the age is a double digit, then fgets is going to read until the last digit and not the newline character. So,you need to read it in case the last char is not \n. If the age is a single digit, the the original program works fine.
You try this following piece of code:
if(stringNum[strlen(arr)-1]=='\n')
stringNum[strlen(arr)-1]='\0';
else
while(getchar()!='\n');
Whenever you enter a two digit age, the newline character which you insert while pressing enter gets stored in the buffer.
What this above piece of code is doing is that, it will check whether the last character of your storage is filled with a newline character, if yes, then it will replace it with the null terminator.
Else, it will keep reading from the buffer until and unless the newline character is removed from the buffer.
PS: If you are using borland then you will have fflush(stdin) to flush out any extra character from the buffer as indicated by PHlFounder, but if you happen to use gcc then this method is very good.
Also you can create a function or macro for this piece of code and call it every time you need, for eg.
void function(char * arr)
{
if(arr[strlen(arr)-1]=='\n')
arr[strlen(arr)-1]='\0';
else
while(getchar()!='\n')
}