Behavior of C scanf() formatting

Viewed 109

Disclaimer. I've seen tons of questions including almost the exact same code snippets, but none seem to answer this question.

For an entry level CS class we are tasked with making a simple program that takes ID, name and age input from a user and saves it to a file. This was simple enough and I got it working pretty quick. The problem is, to get one part, the name input, working properly, I had to "cheat" my way around a problem I met.
The code snippet in question.

int id, age;
char name[40]={0};
printf("ID: ");
scanf("%i",&id);
printf("Name: ");
scanf("%*c");
scanf("%[^\n]%*c",name);
printf("Age: ");
scanf("%i",&age);

This works fine. But this line annoys me; scanf("%*c"); Its only purpose is disposing of a '\n' character lurking in the stream, probably from the previous input. For some reason I feel like this is cheating or that I'm doing something wrong if I have to use this workaround. Any tips appreciated.

2 Answers

But this line annoys me; scanf("%*c"); Its only purpose is disposing of a '\n' character lurking in the stream, probably from the previous input.

scanf() is pretty heavyweight for reading a single character. I would typically go with getchar() for that, and just ignore the result if you don't care what it is.

For your particular case, however, I would go with the suggestion made by @OlafDietsche in comments: insert a space (or a newline or tab) at the beginning of the format string for the next scanf:

scanf(" %[^\n]%*c",name);

That will match any run of zero or more whitespace characters, and thus will eat up your newline, plus any subsequent blank lines and any leading whitespace on the next line that has any non-whitespace. That's the standard behavior for most other field directives anyway, which is why you don't have the same issue with your numeric fields, but it is intentionally not done automatically for %[ and %c fields, so that these can read and return whitespace.

That also means that you don't need to explicitly consume the newline at the end of the name line, either ...

scanf(" %[^\n]",name);

... because it will be consumed automatically by the %i directive in the subsequent scanf() call.

Consider using fget() to read a line of user input @Barmar, then parse.

char buffer[100];
int id, age;
char name[40]={0};

printf("ID: ");
fgets(buffer, sizeof buffer, stdin);
sscanf(buffer, "%i", &id);

printf("Name: ");
fgets(buffer, sizeof buffer, stdin);
sscanf(buffer, "%[^\n]", name);

printf("Age: ");
fgets(buffer, sizeof buffer, stdin);
sscanf(buffer, "%i", &age);

As one's skills improve, add error checking.

printf("ID: ");
if (fgets(buffer, sizeof buffer, stdin) == NULL) Handle_end_of_file();
if (sscanf(buffer, "%i",&id) != 1) Handle_non_numeric_input();
// also research strtol()

printf("Name: ");
if (fgets(buffer, sizeof buffer, stdin) == NULL) Handle_end_of_file();
if (sscanf(buffer, " %99[^\n]",name) != 1) Handle_space_only_name();
// Later, additional code to detect excessively long name 
Related