Getting User Input in C++

Get text from user input using C

You use the wrong format specifier %d- you should use %s. Better still use fgets - scanf is not buffer safe.

Go through the documentations it should not be that difficult:

scanf and fgets

Sample code:

#include <stdio.h>

int main(void)
{
char name[20];
printf("Hello. What's your name?\n");
//scanf("%s", &name); - deprecated
fgets(name,20,stdin);
printf("Hi there, %s", name);
return 0;
}

Input:

The Name is Stackoverflow 

Output:

Hello. What's your name?
Hi there, The Name is Stackov

Requesting user input in C

One of the biggest problems faced by new C programmers is handling user input correctly, especially when using the scanf family of functions. Why? When using scanf you must account for all characters left in the input buffer (stdin here) in the event of a matching failure. Why? When a matching failure occurs, scanf stops processing characters at the point of failure, and the character(s) that caused the failure remain in your input buffer unread, just waiting to bite you on your next attempted input.

Further complicating the issue is how the difference scanf conversion specifiers treat whitespace. Your numeric input specifiers and %s will consume leading whitespace, while the remainder of the conversion specifiers don't. That means if you are taking input with other than a numeric or %s conversion specifier -- you must account for and remove the trailing '\n' before attempting the next character or character class conversion.

That said, whenever you use scanf, it is up to you to check the return so you can determine whether the user canceled input with a manually generated EOF of whether a matching or input failure occurred.

At the bare-minimum, you must check the return and handle any return indicating less than the number of expected conversions took place. In your case, with one conversion to int, you must check that the return is 1 before making use of value. Otherwise you can easily invoke Undefined Behavior. Now just checking whether all conversions occurred does not allow you to discriminate between EOF or matching failure - leaving you without the information needed to proceed making the best you can do is to exit on invalid input, e.g.

#include <stdio.h>

int main (void) {

int value = 0;

while (value >= 0) {
printf("Enter a number: ");
if (scanf("%d",&value) != 1) {
fputs ("error: invalid input\n", stderr);
return 1;
}
printf("The number you entered is %d\n", value);
}

return 0;
}

Example Use/Output

$ ./bin/scanfpos2
Enter a number: 1
The number you entered is 1
Enter a number: 10
The number you entered is 10
Enter a number: foo
error: invalid input

(note: on entry of an invalid integer, all the program can do is end, you do not know whether the input was canceled or whether a matching failure occurred which would allow you to take appropriate action to continue by emptying the input buffer if the failure was a matching failure.)

The proper way to handle input with scanf is to cover all potential error conditions and to gracefully respond to a matching failure by clearing the input buffer of the offending characters allowing you to continue with input. It helps to have a small helper-function to clear stdin rather than having to repeatedly include a clearing loop every where scanf is used in your code. A short example would be:

/** remove all characters that remain in stdin */
void empty_stdin (void)
{
int c = getchar();

while (c != '\n' && c != EOF)
c = getchar();
}

Which simply reads all characters from stdin until the '\n' or EOF is encountered. Combining that with an expanded check of the return of scanf will allow you to handle a matching failure gracefully, e.g.

#include <stdio.h>

/** remove all characters that remain in stdin */
void empty_stdin (void)
{
int c = getchar();

while (c != '\n' && c != EOF)
c = getchar();
}

int main (void) {

int value = 0;

while (value >= 0) {
int rtn; /* variable to store return on scanf */

printf ("Enter a number: ");
rtn = scanf ("%d",&value);
if (rtn == EOF) { /* handle EOF */
fputs ("user canceled input.\n", stderr);
break;
}
else if (rtn == 0) { /* handle matching/input failure */
fputs ("error: invalid input\n", stderr);
empty_stdin();
}
else /* good input - output value */
printf("The number you entered is %d\n", value);
}

return 0;
}

Example Use/Output

$ ./bin/scanfpos2
Enter a number: 1
The number you entered is 1
Enter a number: 10
The number you entered is 10
Enter a number: foo
error: invalid input
Enter a number: -1
The number you entered is -1

Here if a non-integer like foo is entered, it is caught, the characters removed from stdin and the loop prompts again for input.

Look things over. C is not python. Python hides much of the implementation details from you to essentially protect you from instances just as this, but it has its drawbacks as well. With C, nothing is hidden from you. You are given free reign to write to memory you don't own, repeatedly attempt to read form an input buffer after a conversion failure, etc.. You are responsible for the details.

Lastly, all of this is the primary reason taking input with fgets or POSIX getline is recommended for new users. With a sufficiently sized buffer (don't skimp on size), fgets will read a line at a time from the input buffer, preventing offending characters remaining just waiting to bite you again. getline will allocate a buffer of sufficient size no matter how long the line is -- but you are responsible for freeing the memory when you are done with it. Investigate both as alternatives to using scanf. You can always call sscanf on the buffer holding the line after it is read to parse numeric values from it.

Which is the best way to get input from user in C?

Generally, fgets() is considered a good option. It reads whole lines into a buffer, and from there you can do what you need. If you want behavior like scanf(), you can pass the strings you read along to sscanf().

The main advantage of this, is that if the string fails to convert, it's easy to recover, whereas with scanf() you're left with input on stdin which you need to drain. Plus, you won't wind up in the pitfall of mixing line-oriented input with scanf(), which causes headaches when things like \n get left on stdin commonly leading new coders to believe the input calls had been ignored altogether.

Something like this might be to your liking:

char line[256];
int i;
if (fgets(line, sizeof(line), stdin)) {
if (1 == sscanf(line, "%d", &i)) {
/* i can be safely used */
}
}

Above you should note that fgets() returns NULL on EOF or error, which is why I wrapped it in an if. The sscanf() call returns the number of fields that were successfully converted.

Keep in mind that fgets() may not read a whole line if the line is larger than your buffer, which in a "serious" program is certainly something you should consider.

How do I get fgets to receive user input?

scanf() leaves \n made by the press to Return/Enter in stdin.

The scanf() calls in your code catch those left newline character from the previous calls because they skip leading white space (such as tab, newline or just plain white space) in stdin, but opposed to that fgets() does not skip leading white space characters.

This newline character is fetched by fgets(), which stops consuming input from stdin until it encounters a newline.

The result is that only the \n newline character is taken by the call to fgets().

To catch the abandoned newline you could either use getchar(); or scanf("%*c"); before the call to fgets():

scanf("%s", rating);
printf("\nYou said %s. Please provide some feedback.", rating);

getchar(); // catching newline character.

fgets(feedback, 50, stdin);

Side note: Don´t intermix calls to fgets() with scanf(). Catch either all input as strings by using fgets() and thereafter parse them with sscanf() or use scanf() consistently.

Issues with scanf() and accepting user input

scanf("%c", &findChar); reads the next character pending in the input stream. This character will be the newline entered by the user that stopped the previous conversion, so findChar will be set to the value '\n', without waiting for any user input and printf will output this newline without any other visible effect.

Modify the call as scanf(" %c", &findChar) to ignore pending white space and get the next character from the user, or more reliably write a loop to read the read and ignore of the input line.

Note also that scanf("%[^\n]s", userIn); is incorrect:

  • scanf() may store bytes beyond the end of userIn if the user types more than 29 bytes of input.
  • the s after the ] is a bug, the conversion format for character classes is not a variation of the %s conversion.

Other problems:

  • void is not a proper type for the return value of the main() function.
  • the <stdio.h> header is required for this code.

Here is a modified version:

#include <stdio.h>

int main() {
char userIn[30];
int c;
char findChar;
int i, found;

printf("Please enter a string: ");
if (scanf("%29[^\n]", userIn) != 1) {
fprintf(stderr, "Input failure\n");
return 1;
}
/* read and ignore the rest of input line */
while ((c = getchar()) != EOF && c != '\n')
continue;

printf("Please enter a character to search for: ");
if (scanf("%c", &findChar) != 1) {
fprintf(stderr, "Input failure\n");
return 1;
}

printf("Searching for '%c'\n", findChar);
found = 0;
for (i = 0; userIn[i] != '\0'; i++) {
if (userIn[i] == findChar) {
found++;
printf("found '%c' at offset %d\n", c, i);
}
}
if (!found) {
printf("character '%c' not found\n", c);
}
return 0;
}

What is the simplest way of getting user input in C?

The simplest "correct" way is probably this one, taken from Bjarne Stroustrup's paper Learning Standard C++ As A New Language.

(Note: I changed Bjarne's code to check for isspace() instead of just end of line. Also, due to @matejkramny's comment, to use while(1) instead of while(true)...and so long as we're being heretical enough to edit Stroustrup's code, I've subbed in C89 comments instead of C++ style too. :-P)

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>

void quit() /* write error message and quit */
{
fprintf(stderr, "memory exhausted\n");
exit(1);
}

int main()
{
int max = 20;
char* name = (char*) malloc(max); /* allocate buffer */
if (name == 0) quit();

printf("Enter a file name: ");

while (1) { /* skip leading whitespace */
int c = getchar();
if (c == EOF) break; /* end of file */
if (!isspace(c)) {
ungetc(c, stdin);
break;
}
}

int i = 0;
while (1) {
int c = getchar();
if (isspace(c) || c == EOF) { /* at end, add terminating zero */
name[i] = 0;
break;
}
name[i] = c;
if (i == max - 1) { /* buffer full */
max += max;
name = (char*) realloc(name, max); /* get a new and larger buffer */
if (name == 0) quit();
}
i++;
}

printf("The filename is %s\n", name);
free(filename); /* release memory */
return 0;
}

That covers:

  • skipping whitespace until you reach character input
  • expanding the string buffer dynamically to fit arbitrary size strings
  • handling conditions of when memory can't be allocated

Are there simpler but broken solutions, which might even run a bit faster? Absolutely!!

If you use scanf into a buffer with no limit on the read size, then your input exceeds the size of the buffer, it will create a security hole and/or crash.

Limiting the size of the reading to, say, only 100 unique characters of a filename might seem better than crashing. But it can be worse; for instance if the user meant (...)/dir/foo/bar.txt but you end up misinterpreting their input and overwriting a file called bar.t which perhaps they cared about.

It's best to get into good habits early in dealing with these issues. My opinion is that if your requirements justify something close-to-the-metal and "C-like", it's well worth it to consider the jump to C++. It was designed to manage precisely these concerns--with techniques that are robust and extensible, yet still perform well.



Related Topics



Leave a reply



Submit