Reputation: 65
This is a seemingly simple question that I have not been able to answer for far too long:
I am trying to read input from a user in a C program using fgets(). However, I am running into the problem that if the user enters more characters than fgets() is set to read, the next call to read a string from the user automatically reads the remaining characters in the stdin buffer, and this is NOT behavior I want.
I have tried many ways to clear the stdin stream, and while I know something like
while(getchar()!='\n');
will work, this requires the user to hit enter an additional time which is not something I want.
The structure of the code looks something like this:
void read_string(char *s, int width){
fgets(s,width,stdin);
clear_stdin();
.
.
}
while (1){
read_string()
.
.
}
But I cannot get a clear_stdin() function that works desirably. How on earth can I clear the stdin, without having the user needlessly need to hit enter twice?
Upvotes: 3
Views: 5282
Reputation: 755114
To achieve what you want — reading and ignoring extra characters up to a newline if the buffer you supplied is over-filled — you need to conditionally read up to the newline, only doing so if there isn't a newline already in the input buffer that was read.
void read_string(char *s, int width)
{
if (fgets(s, width, stdin) != 0)
{
size_t length = strlen(s);
if (length > 0 && s[length-1] != '\n')
{
int c;
while ((c = getchar()) != '\n' && c != EOF)
;
}
/* Use the input data */
}
else
/* Handle EOF or error */
}
The other part of the technique is to make sure that you use a big enough buffer that it is unlikely that anyone will overflow it. Think in terms of char buffer[4096];
as an opening bid; you can increase it if you prefer. That makes it unlikely that anyone will (be able to) type enough data on a single line to overflow the buffer you provide, thus avoiding the problem.
Another option is to use POSIX getline()
. It reads a line into allocated space, allocating more space until the data fits (or you run out of memory). It has at least one other major advantage over fgets()
— it reports the number of characters in the line it read, which means it is not confused by null bytes ('\0'
, usually typed as Control-@) in the input. By contrast, you can't tell whether there was any data entered after the first null byte with fgets()
; you have to assume that the input stopped at the first null byte.
Note that the simple loop shown in the question (while (getchar() != '\n');
) becomes infinite if it encounters EOF before reading a newline.
Upvotes: 4
Reputation: 1
You cannot clear stdin
in a portable way (because no function from <stdio.h>
is specified doing that). BTW, stdin can usually be not only a terminal, but also a redirection or a pipe (or even perhaps some socket). Details matter of course (e.g. your operating system and/or running environment).
You could avoid stdio and use operating system specific ways to deal with standard input (e.g. working at the file descriptor level on POSIX systems).
On Linux (specifically) you might read more about the Tty demystified, and code low level code based on such knowledge. See termios(3). Consider using readline(3).
You could use (on Linux at least) getline(3) to read a heap-allocated line buffer.
Upvotes: 4
Reputation: 433
while ((getchar()) != '\n');
This will not always work...(but on the bright side, the cases in which it doesn't are just as portable as the cases in which it does). But if stdin has not been redirected, the terminal char of the user's input, unless a manual EOF
, will usually be a newline. After you extract what you expect, assuming you don't expect the \n
, you can drain what's there up until(and including) the '\n', and then iterate anew. As others have suggested, there are higher level interfaces to deal with this minutia more reliably than manual fringe case handling most of the time.
More Details on Challenge and Solutions This link contains the cardinal sin of "C\C++" in its heading, which doesn't exist as an entity. Rest assured, separate C examples are given, discrete from alternate C++ ones.
Upvotes: 2