C / Editorial solution guide
Fix Input Mismatches and Leftover Newlines
Separate complete-line acquisition from numeric parsing so failed conversions and leftover delimiters cannot silently change the next record.
Read-only knowledge base · No public comments
Recognize the symptoms
After a numeric scanf call, a following fgets appears to read an empty name. Alternatively, invalid text makes a retry loop repeat forever, or a number prefix such as 12x is accepted unexpectedly. The behaviors point to stream consumption and validation policy, rather than a missing delay between reads.
Understand the likely causes
- A successful %d conversion generally leaves the following newline unread. A failed matching conversion can leave the offending characters in the stream. Ignoring scanf’s conversion count then permits stale values or repeated failures. An unbounded string conversion is unsafe, and fflush(stdin) is not a portable C17 input-clearing solution.
1. Trace exactly what was consumed
Write the synthetic input as visible records, for example a number line and a name line. Mark which characters each read consumes and which remain. fgets does not invent an empty line: it may legitimately return the newline left by the previous numeric conversion. Distinguish that case from end of file by checking its return value before examining the buffer.
2. Choose one bounded record policy
For this repair, one physical line is one record, each must fit its buffer including newline and terminator, and numeric lines must contain decimal digits with no extra suffix. Longer records are rejected rather than split. A production reader may accept a final line without newline, but that requires an explicit end-of-file policy; the controlled example requires newline.
3. Acquire first, then convert and validate
The complete example uses a temporary stream holding known lab records. It checks successful bounded reads and captured newlines, parses with strtol, checks no-digit, suffix, ERANGE, and domain failures, then reads the name independently. The name is treated as data through %s. No unbounded conversion or platform-specific input flushing is required.
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char number[16], name[16];
FILE *input = tmpfile();
if (input == NULL) return 1;
if (fputs("12\nAda\n", input) == EOF) { fclose(input); return 1; }
rewind(input);
if (fgets(number, sizeof number, input) == NULL) { fclose(input); return 1; }
char *newline = strchr(number, '\n');
if (newline == NULL) { fclose(input); return 1; }
*newline = '\0';
char *end;
errno = 0;
long value = strtol(number, &end, 10);
if (end == number || *end != '\0' || errno == ERANGE || value < 0 || value > 100) {
fclose(input); return 1;
}
if (fgets(name, sizeof name, input) == NULL) { fclose(input); return 1; }
newline = strchr(name, '\n');
if (newline == NULL) { fclose(input); return 1; }
*newline = '\0';
printf("count=%ld name=%s\n", value, name);
return fclose(input) == 0 ? 0 : 1;
}
count=12 name=Ada
The first fgets consumes the number line completely, so the next read obtains Ada.
4. If retaining scanf, check and recover deliberately
If an existing interface keeps scanf, require the expected assignment count and define how the remainder of the current record is consumed after success or matching failure. A single getchar is insufficient when trailing spaces or longer garbage exist. Use a controlled loop through newline or EOF and distinguish I/O failure. Do not add a trailing whitespace directive blindly: it can wait for a later non-whitespace character.
5. Keep syntax and application range separate
A record can be syntactically numeric yet outside the application domain. Check representational range before narrowing from long to int, then enforce limits such as zero through one hundred. A caller should update its stored count only after every check succeeds. On rejection, preserve the previous valid state and report which record needs correction rather than continuing with a partially parsed prefix.
Verify the fix
The complete lab program must print count=12 name=Ada. Test a nonnumeric line, 12x, 101, an empty line, end of input, and a line at or beyond buffer capacity; none may cause stale data to be accepted. Verify that the next record begins at its intended boundary after every accepted or rejected record under the chosen recovery policy.
Keep it from recurring
- Prefer one bounded line reader and a separate parser per record type. Document newline and truncation handling, always check library return values, and test boundary-length inputs. Keep external text out of format strings and use %ld for long values and the other specifiers matching their actual types.