This question already has an answer here:
-
scanf() leaves the new line char in the buffer
4 answers
For some reason scanf
reads the character but the program doesn't continue past the while loop used to validate the character even though the character is valid. Sometimes it works and other times it doesn't. The weird thing is I don't do anything different. Any ideas?
do
{
printf("Enter \"p\" if you want to sort and shuffle a list of players or enter \"s\" if you want to sort a list of slots: "); // prompt
scanf("%c", &tmp);
} while (tmp != 'p' && tmp != 's');
new code:
printf("Enter 'p' if you want to sort and shuffle a list of players or enter 's' if you want to sort a list of slots:"); // prompt
tmp = getchar();
printf("%c ",tmp);
if (tmp == 'p')
{
size = readPlayerFile(players); // calling readPlayerFile function
shufflePlayers(players, size); // call shufflePlayers function
sortPlayers(players, size); // call sortPlayers function
}
else if (tmp == 's')
{
printf("hello");
size = readSlotFile(slots); // calling readSlotFile function
sortSlot(slots, size); // call sortSlots function
}
The simplest option is to add a leading space to the format string in scanf()
. For example, if you want to prompt the user to enter a character again when bad input is provided, you could do this:
#include <stdio.h>
int main(void)
{
char tmp;
printf("Enter \"p\" if you want to sort and shuffle a list of players "
"or enter \"s\" if you want to sort a list of slots: "); // prompt
while (scanf(" %c", &tmp) != 1 || (tmp != 'p' && tmp != 's')) {
printf("Please enter \"p\" or \"s\": ");
}
printf("You chose %c\n", tmp);
printf("Choose \"a\"scending or \"d\"escending sort: ");
while (scanf(" %c", &tmp) != 1 || (tmp != 'a' && tmp != 'd')) {
printf("Please enter \"a\" or \"d\": ");
}
printf("You chose %c\n", tmp);
return 0;
}
There is a problem with this simple approach though. If the user enters "aaap"
then the resulting output would be:
Enter "p" if you want to sort and shuffle a list of players or enter "s" if you want to sort a list of slots: aaap
Please enter "p" or "s": Please enter "p" or "s": Please enter "p" or "s": You chose p
Choose "a"scending or "d"escending sort:
Or, worse, if the user enters a correct first character followed by other characters, such as "sfffa"
:
Enter "p" if you want to sort and shuffle a list of players or enter "s" if you want to sort a list of slots: sfffa
You chose s
Choose "a"scending or "d"escending sort: Please enter "a" or "d": Please enter "a" or "d": Please enter "a" or "d": You chose a
Because scanf()
leaves unmatched characters in the input stream, those characters must be dealt with before reading the input stream again. In the simple solution using " %c"
, the initial space causes scanf()
to skip over leading white-space characters, but any other characters will be picked up. And in the real world you can't count on the user cooperating by providing well-behaved input.
One typical, portable, solution is to use getchar()
in a loop to clear the input stream after an input operation. You may need to change the input loop logic a bit to do this:
#include <stdio.h>
...
char tmp;
int c;
int scanf_ret;
do {
printf("Enter \"p\" if you want to sort and shuffle a list of players "
"or enter \"s\" if you want to sort a list of slots: "); // prompt
scanf_ret = scanf("%c", &tmp);
while ((c = getchar()) != '\n' && c != EOF) {
continue; // discard extra characters
}
} while (scanf_ret != 1 || (tmp != 'p' && tmp != 's'));
After the call to scanf()
, a loop is entered that reads and discards any characters remaining in the input stream. Note that c
is an int
, and that EOF
is tested for explicitly in the loop. The getchar()
function may return EOF
in the event of an error, or if the user signals EOF
from the keyboard, or if input has been redirected from a file. Failure to test for EOF
in such circumstances would result in an infinite loop condition.
This solution works, even if the user enters unpredictable input, since the input stream is always cleared after getting input. And note that there is no need for the leading space in the format string. Another solution would be to use fgets()
to read a line of input into a buffer, and sscanf()
to parse the buffer.
#include <stdio.h>
#include <stdlib.h>
...
char buffer[1000];
char tmp;
int sscanf_ret;
do {
printf("Enter \"p\" if you want to sort and shuffle a list of players "
"or enter \"s\" if you want to sort a list of slots: "); // prompt
if (fgets(buffer, sizeof buffer, stdin) == NULL) {
fprintf(stderr, "Error in fgets()\n");
exit(EXIT_FAILURE);
}
sscanf_ret = sscanf(buffer, "%c", &tmp);
} while (sscanf_ret != 1 || (tmp != 'p' && tmp != 's'));
The fgets()
function gets an entire line of input, through the newline, or gets (sizeof buffer) - 1
characters from the input stream if the buffer is not large enough to hold all of the characters in the input stream and a \0
terminator. By providing a generously sized buffer, this should work fine. A user could still enter a very large number of characters, and while this will not overflow the buffer, characters would be left behind in the input stream for the next read operation to find. To avoid such issues, if there are extra characters left behind, the input stream should be cleared after the call to fgets()
using the getchar()
loop from above.
All of these techniques have their place. Clearly the last two are much more robust than the first, and as it is unlikely that a user will enter 999 characters at the input prompt (including the newline), they are almost on equal footing. But, to avoid all surprise, explicitly clearing the input stream after getting user input is best.
Firstly, scanf()
leaves a \n
in the input buffer, and it remains in the input buffer the next time it is called. You need to add a space to your format specifier:
scanf(" %c", &tmp)
To skip white space characters, which can be newlines, blank spaces or tabs.
Secondly, you need to check return of scanf()
, to ensure only one character was found.
Your code can then look like this:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
char temp;
do {
printf("Enter \"p\" if you want to sort and shuffle a list of players or enter \"s\" if you want to sort a list of slots: "); // prompt
if (scanf(" %c", &tmp) != 1) {
printf("Invalid character\n");
exit(EXIT_FAILURE);
}
} while (tmp != 'p' && tmp != 's');
return 0;
}