I am aware that there are numerous posts about why the trailing newline character problem occurs and how to fix it. However, of all the solutions out there, the use scanf(" %c", ch)
method is the most common one. Yet no one discusses why it works! What does the extra whitespace do differently in this case?

- 327
- 2
- 15
-
4From [`scanf`](https://en.cppreference.com/w/c/io/fscanf): _"any single whitespace character in the format string consumes all available consecutive whitespace characters from the input (determined as if by calling `isspace` in a loop). Note that there is no difference between `"\n"`, `" "`, `"\t\t"`, or other whitespace in the format string."_ - If you've read that, but didn't understand it, can you please point out what part you don't understand. – Ted Lyngmo Oct 15 '22 at 03:57
-
It will consume all leading whitespace then it will store the first non-whitespace into `ch`. Trailing whitespace (as in after what we read into `ch`) would be dealt with in the next call, either the same way or implicitly as most of the format strings ignore leading whitespace. – Allan Wind Oct 15 '22 at 04:10
-
@TedLyngmo Okay, I actually hadn't read that (probably should have, apologies). But that brings me to my next question, does that mean this is a bulletproof method of avoiding the trailing newline character problem, or are there better more suitable ways of handling this problem? – Prithvidiamond Oct 15 '22 at 05:37
-
"bulletproof" and "scanf" are usually not placed in the same sentense. It'd be better to read a line and then to `sscanf` it. – Ted Lyngmo Oct 15 '22 at 05:43
-
2@Prithvidiamond - the man pages are your friend. A bit cryptic the first time you look at them, but soon you realize they give you precisely the information you need to correctly use any function, including the header file(s) needed and any define (Feature Test Macros) needed. [man7.org - Linux man pages online](https://man7.org/linux/man-pages/index.html) is nicely done and browseable by section (man 1, man 2, man 3 ...) and alphabetically. You can also just type into your search box, e.g. `"man7 scanf"` and it will take you to the needed page, top 1 or 2 links every time. – David C. Rankin Oct 15 '22 at 05:50
-
1Spend the time to learn `scanf()`, then you will know why you should use `fgets()` followed by `sscanf()` and avoid all the pitfalls of `scanf()` alone. Pay attention when reading to the different between *Matching-Failure* and *Input-Failure* and what happens to characters in the input buffer from the point of any *Matching-Failure* and what is required on your part if you actually do want to use `scanf()` correctly. (all of which will lead you to `fgets()`) – David C. Rankin Oct 15 '22 at 05:55
-
@TedLyngmo Hmmm, could you elaborate, I didn't understand what you meant by reading a line before using `sscanf`. Don't I need `sscanf` to get input? EDIT: nevermind, @DavidC.Rankin elaborated on what it means – Prithvidiamond Oct 15 '22 at 05:56
-
@DavidC.Rankin hmm, yes I should use the man pages a lot more I have only been using Linux for a short time now but I am slowly warming my way up to learning to use and get comfortable with everything it offers. Thanks for the advice, I will certainly look up the man pages for `scanf`, `sscanf` and `fgets` – Prithvidiamond Oct 15 '22 at 05:58
-
2You avoid all pitfalls in `scanf()` by reading all user input with `fgets()` into a sufficiently sized buffer (character array) and then using the buffer in `sscanf()` to get the information you need. The key is `fgets()` consumes an entire line of input at a time, and there is no possibility of characters remaining in the input buffer unread. `sscanf()` works just like `scanf()` it just reads the data from the buffer you fill with `fgets()`. – David C. Rankin Oct 15 '22 at 05:58
-
1@Prithvidiamond DavidC.Rankin covered it. – Ted Lyngmo Oct 15 '22 at 05:59
-
1@TedLyngmo yes, I just saw it, thank you! – Prithvidiamond Oct 15 '22 at 06:00
-
1Good luck with your coding! – David C. Rankin Oct 15 '22 at 06:00
-
"no one discusses why it works"? Absurd. I see a discussion of why it works accompanying pretty much *every* appearance of that recommendation. Of course, here on SO is where I typically see those. Perhaps we're a better resource than whatever other sources of advice you've been following. – John Bollinger Oct 16 '22 at 15:22
-
@DavidC.Rankin, The `scanf()` issues that `fgets()` + `sscanf()` address are *some* of the more common ones revolving around line-based input, especially interactive input. It is in no way true that `fgets()` + `sscanf()` "avoid[s] all pitfalls in scanf()". – John Bollinger Oct 16 '22 at 15:34
-
@JohnBollinger not to split hairs, but the only connotation of this question where it wouldn't be true is if the user is redirecting a file as input on `stdin`. There is zero mention about redirection in the question. Additionally, the "all" in my comment addresses "reading all user input". So while I agree absolutes are to be used with caution, it is very much a certainty that using `fgets()` + `sscanf()` will avoid ALL pitfalls with `scanf()` which is no longer part of the equation. Common format string problems will remain, but no unread characters will. – David C. Rankin Oct 16 '22 at 21:51
1 Answers
Here is the explanation from the C Standard:
7.22.6.2 The
fscanf
functionSynopsis
#include <stdio.h> int fscanf(FILE * restrict stream, const char * restrict format, ...);
Description
2 The
fscanf
function reads input from the stream pointed to bystream
, under control of the string pointed to byformat
that specifies the admissible input sequences and how they are to be converted for assignment, using subsequent arguments as pointers to the objects to receive the converted input. If there are insufficient arguments for the format, the behavior is undefined. If the format is exhausted while arguments remain, the excess arguments are evaluated (as always) but are otherwise ignored.3 The format shall be a multibyte character sequence, beginning and ending in its initial shift state. The format is composed of zero or more directives: one or more white-space characters, an ordinary multibyte character (neither
%
nor a white-space character), or a conversion specification. Each conversion specification is introduced by the character%
. After the%
, the following appear in sequence: [...]4 The
fscanf
function executes each directive of the format in turn. When all directives have been executed, or if a directive fails (as detailed below), the function returns. Failures are described as input failures (due to the occurrence of an encoding error or the unavailability of input characters), or matching failures (due to inappropriate input).5 A directive composed of white-space character(s) is executed by reading input up to the first non-white-space character (which remains unread), or until no more characters can be read. The directive never fails.
Hence the initial space in " %c"
is a directive that consumes any pending white-space characters in the input stream, including the newline left pending from a previous call to scanf
. Note that "\n%c"
would behave exactly the same way but might be more confusing as unsuspecting readers might expect scanf()
to require a newline to be read and fail on other characters.
An alternative approach to ensure only a pending newline is consumed is this format: "%*1[\n]%c"
. scanf
will return 0
if the next character from stdin
is not a newline, and this format allows for a white-space character to be retrieved from the user after the newline, such as ' '
and '\t'
. If the user hits the Enter key immediately, scanf
will get this newline character '\n'
into the char
destination variable of the %c
conversion, but there will be no pending newline in stdin
after scanf()
returns.
In spite of these considerations, it is recommended to avoid scanf()
and fscanf()
and instead read input from the user one line at a time with fgets()
and parse it with appropriate functions, sscanf()
or other more reliable functions such as strtol()
that provide better error detection.

- 131,814
- 10
- 121
- 189