Should I use fgets
or formatted scanf
like scanf("s", foo)
.
Excepted that scanf
does not read blank characters, which can be solved and do more stuffs with scanset, then why I should use fgets
instead of scanf
?
Any help would be appreciated.
Edit
One more thing I want to ask is: even when we use fgets
what happen if user enter characters more than boundary (I mean a lot of characters), does it lead to buffer overflow? Then how to deal with it?
CodePudding user response:
On most operating sytems, user input is, by default, line-based. One reason for this is to allow the user to press the backspace key to correct the input, before sending the input to the program.
For line-based user input, it is meaningful and intuitive for a program to read one line of input at a time. This is what the function fgets
does (provided that the buffer is large enough to store the entire line of input).
The function scanf
, on the other hand, normally does not read one line of input at a time. For example, when you use the %s
or %d
conversion format specifier with scanf
, it will not consume an entire line of input. Instead, it will only consume as much input as matches the conversion format specifier. This means that the newline character at the end of the line will normally not be consumed (which can easily lead to programming bugs). Also, scanf
called with the %d
conversion format specifier will consider input such as 6sldf23dsfh2
as valid input for the number 6
, but any further calls to scanf
with the same specifier will fail, unless you discard the remainder of the line from the input stream.
This behavior of scanf
is counter-intuitive, whereas the behavior of fgets
is intuitive, when dealing with line-based user input.
After using fgets
, you can use the function sscanf
on the string, for parsing the contents of an individual line. This will allow you to continue using scansets. Or you can parse the line by some other means. Either way, as long as you are using fgets
instead of scanf
for reading the input, you will be handling one line of input at a time, which is the natural and intuitive way to deal with line-based user input.
When we use
fgets
what happen if user enter characters more than boundary (I mean a lot of characters), does it lead to buffer overflow? Then how to deal with it?
If the user enters more characters than fit in the buffer as specified by the second fgets
function argument, then it will not overflow the buffer. Instead, it will only extract as many characters from the input stream as fit in the buffer. You can determine whether the entire line was read by checking whether the string contains a newline character '\n'
at the end.
CodePudding user response:
This is a commonly discussed topic, full of opinion but interesting none the less. I have observed that a large majority of those that have already responded to similar questions on this site fall on the side of fgets()
. I am one of them. I find fgets()
to be much better to use for user input than scanf()
with few exceptions. scanf()
is considered by many as as sub-optimal method for handling user input. For example
"...it will tell you whether it succeeded or failed, but can tell you only approximately where it failed, and not at all how or why. You have very little opportunity to do any error recovery."
(jamesdlin). But in the interest of attempting balance, will start off citing this discussion.
For user input that comes from stdin
, i.e. keyboard input, fgets()
will be a better choice. It is much more forgiving in that the string it reads can be fully validated before conversion is attempted
One of the few times using a form of scanf(): fscanf() would be okay to use might be when converting input from a very controlled source, i.e. from reading a strictly formatted file with repeating predictable fields.
For more discussion, this comparison of the two highlights additional advantages and disadvantages of both.
Edit: to address OP additional question about overflow:
"One more thing I want to ask is: even when we use fgets what happen if user enter characters more than boundary (I mean a lot of characters), does it lead to buffer overflow? Then how to deal with it?"
[fgets()](https://www.tutorialspoint.com/c_standard_library/c_function_fgets.htm is nicely designed to prevent buffer overflow, simply by using its parameters properly, eg:
char buffer[100] = {0};
...
while fgets(buffer, sizeof buffer, stdin);
This prevents input greater than the buffer size from being processed, thus preventing the overflow.
even using scanf()
preventing buffer overflow is pretty straight forward: Use a width specifier in the format string. If you want to read input for example and limit input size from user to 100 characters max, the code would include the following:
char buffer[101] = {0};// includes space for 100 1 for NULL termination
scanf("0s", buffer);
^^^ width specifier
However with numbers, overflow is not so nice using scanf()
. To demonstrate, use this simple code, inputting the two values indicated in comment one per run:
int main(void)
{
int val = 0;
// test with 2147483647 & 2147483648
scanf("%d", &val);
printf("%d\n", val);
return 0;
}
For the second value, my system throws the following:
NON-FATAL RUN-TIME ERROR: "test.c", line 11, col 5, thread id 22832: Function scanf: (errno == 34 [0x22]). Range error
`
Here you need to read in a string, then follow with a string to number conversion using one of the strto_()
functions: strtol(), strtod(), ...). Both include the ability to test for overflow before causing a run-time warning or error. Note that using atoi()
, atod()
will not protect from overflow either.
CodePudding user response:
If you have for example a character array declared like
char s[100];
and want to read a string that contains embedded spaces then you can use either scanf
the following way
scanf( "