Getting Input from User Using Cin

Getting input from user using cin

You need to allocate space for the char array into which you want to read the Name. char *Name; will not work as it only declares a char pointer not a char array. Something like char Name[30];

Also the cin << only allows us to enter one word into a string (char name[30]).

However, there is a cin function that reads text containing blanks.

cin.get(name, MAX)  

get will read all characters including spaces until Max characters have
been read or the end of line character (‘\n’) is reached and will put them
into the name variable.

how to make cin only take integer inputs

If std::istream::operator >> fails, it will set failbit. Therefore, you should check failbit (for example by calling std::cin.fail()) to see whether the conversion was successful, before using the result of the conversion.

If the conversion fails due to bad input, then the next call to std::istream::operator >> will automatically fail due to failbit being set. That is why you are getting stuck in an infinite loop. If you want to attempt input again after a conversion failure, you will first have to clear failbit, by using the function std::cin.clear().

Also, you will have to discard the bad input that caused the conversion to fail, because otherwise, the next time you call std::istream::operator >>, the conversion will fail again for the same reason. In order to clear the bad input, you can use std::cin.ignore(), like this:

std::cin.ignore( std::numeric_limits<std::streamsize>::max(), '\n' );

In order to use std::numeric_limits, you will have to #include <limits>.

After performing these fixes on your code, it should look like this:

#include <iostream>
#include <limits>

using namespace std;

int main()
{
int num[100];
int n;
double sum, average;
bool input_ok;

//repeat until input is valid
do
{
cout << "How many numbers will you input? ";
cin >> n;

if ( cin.fail() )
{
cout << "Error: Conversion to integer failed!\n";

input_ok = false;
}
else if ( n > 100 || n <= 0 )
{
cout << "Error: Number should in range of (1 to 100) only!\n";
input_ok = false;
}
else
{
input_ok = true;
}

//clear failbit
cin.clear();

//discard remainder of line
cin.ignore( numeric_limits<streamsize>::max(), '\n' );
} while ( !input_ok );

for ( int i = 0; i < n; ++i )
{
do
{
cout << i + 1 << ". Enter number: ";
cin >> num[i];

if ( cin.fail() )
{
cout << "Error: Conversion to integer failed!\n";

input_ok = false;
}
else
{
input_ok = true;
}

//clear failbit
cin.clear();

//discard remainder of line
cin.ignore( numeric_limits<streamsize>::max(), '\n' );
} while ( !input_ok );

sum += num[i];
}

average = sum / n;
cout << "Average = " << average;
}

This program has the following behavior:

How many numbers will you input? 200
Error: Number should in range of (1 to 100) only!
How many numbers will you input? -31
Error: Number should in range of (1 to 100) only!
How many numbers will you input? test
Error: Conversion to integer failed!
How many numbers will you input? 4abc
1. Enter number: 1
2. Enter number: 2
3. Enter number: 3
4. Enter number: 4
Average = 2.5

As you can see, the program now works in that it can now handle bad input such as test. It rejects that input and reprompts the user for new input.

However, one problem with this program is that it accepts 4abc as valid input for the number 4. It would probably be appropriate to reject such input instead. One way to fix this would be to inspect the remainder of the line, instead of simply discarding it.

Another issue is that this solution contains a lot of code duplication. Apart from the range check, both do...while loops are nearly identical. Therefore, it would be better to put this loop into a function, which can be called from several places in your code.

However, I generally don't recommend that you use std::istream::operator >>, because its behavior is not always intuitive. For example, as already pointed out above:

  1. It does not always read a whole line of input, so that you must explicitly discard the remainder of the line.
  2. It accepts 4abc as valid input for the number 4.

In my experience, if you want proper input validation of integer input, it is usually better to write your own function that reads a whole line of input using std::getline and converts it with std::stoi. If the input is invalid, then the function should automatically reprompt the user.

In my example below, I am calling this function get_int_from_user.

If you want to additionally ensure that the input is in a certain range, then you can call the function get_int_from_user in an infinite loop, and break out of that loop once you determine that the input is valid.

#include <iostream>
#include <string>
#include <sstream>
#include <cctype>

int get_int_from_user( const std::string& prompt );

int main()
{
int nums[100];
int n;
double sum;

//repeat loop forever, until input is good
for (;;) //equivalent to while(true)
{
n = get_int_from_user( "How many numbers will you input? " );

if ( 1 <= n && n <= 100 )
//input is good
break;

std::cout << "Error! Number should in range of (1 to 100) only.\n";
}

//read one number per loop iteration
for( int i = 0; i < n; i++ )
{
std::ostringstream prompt;

prompt << "Enter number #" << i + 1 << ": ";

nums[i] = get_int_from_user( prompt.str() );

sum += nums[i];
}

std::cout << "Average: " << sum / n << '\n';
}

int get_int_from_user( const std::string& prompt )
{
std::string line;
std::size_t pos;
int i;

//repeat forever, until an explicit return statement or an
//exception is thrown
for (;;) //equivalent to while(true)
{
//prompt user for input
std::cout << prompt;

//attempt to read one line of input from user
if ( !std::getline( std::cin, line ) )
{
throw std::runtime_error( "unexpected input error!\n" );
}

//attempt to convert string to integer
try
{
i = std::stoi( line, &pos );
}
catch ( std::invalid_argument& )
{
std::cout << "Unable to convert input to number, try again!\n";
continue;
}
catch ( std::out_of_range& )
{
std::cout << "Out of range error, try again!\n";
continue;
}

//The remainder of the line is only allowed to contain
//whitespace characters. The presence of any other
//characters should cause the entire input to get rejected.
//That way, input such as "6sdfj23jlj" will get rejected,
//but input with trailing whitespace will still be accepted
//(just like input with leading whitespace is accepted by
//the function std::stoi).
for ( ; pos < line.length(); pos++ )
{
if ( !std::isspace( static_cast<unsigned char>(line[pos]) ) )
{
std::cout << "Invalid character found, try again!\n";

//we cannot use "continue" here, because that would
//continue to the next iteration of the innermost
//loop, but we want to continue to the next iteration
//of the outer loop
goto continue_outer_loop;
}
}

//input is valid
return i;

continue_outer_loop:
continue;
}
}

This program has the following behavior:

How many numbers will you input? 200
Error! Number should in range of (1 to 100) only.
How many numbers will you input? -31
Error! Number should in range of (1 to 100) only.
How many numbers will you input? test
Unable to convert input to number, try again!
How many numbers will you input? 4abc
Invalid character found, try again!
How many numbers will you input? 4
Enter number #1: 1
Enter number #2: 2
Enter number #3: 3
Enter number #4: 4
Average: 2.5

As you can see, it now correctly rejects the input 4abc.

I believe that using the function get_int_from_user makes the code in main much cleaner.

Note that the code above uses one goto statement. Under most circumstances, you should avoid using goto, but for breaking out of nested loops, it is considered appropriate.

The best way to capture user input int with error handling loop

Reading from a stream using operator >> takes as many characters from the stream as the target type accepts; the rest will remain in the stream for subsequent reads. If the input has a format error (e.g. a leading alphabetical characters when an integer is expected), then an error-flag is set, too. This error-flag can be checked with cin.fail(). It remains set until it gets explicitly cleared. So if your code is...

int user_choice;
cin >> user_choice;

and if you then enter something that is not a number, e.g. asdf, then user_choice has an undefined value, an error-flag cin.fail() is (and reamins) set. So any subsequent read will fail, too.

To overcome this, you have to do three things:

First, check the error-flag. You can do this either through calling cin.fail() after a read attempt of through checking the return value of the expression (cin >> user_choice), which is the same as calling cin.fail().

Second, in case of an error, you need to clear the error-flag using cin.clear(). Otherwise, any attempt to read in anything afterwards will fail.

Third, if you want to continue with reading integral values, you need to take the invalid characters from the stream. Otherwise, you will read in asdf into a variable of type integer again and again, and it will fail again and again. You can use cin.ignore(numeric_limits<streamsize>::max(),'\n'); to take all characters until EOF or an end-of-line from the input buffer.
The complete code for reading an integral value with error-handling could look as follows:

int readNumber() {

int result;

while (!(cin >> result)) {
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(),'\n');
cout << "Input is not a number." << std::endl;
}

return result;
}

How do I efficiently foolproof user input while using cin to ask for an int?

I recommend you do not use an integer to store a "Yes" or "No" answer and use a string instead. This way you can save yourself some lines of code withcin.fail() , cin.ignore() and cin.clear():

 int main() {

string trap;
cout << "----------------------------------" << endl;
cout << "| Would you like to pick a card? |" << endl;
cout << "| Type 1 for yes or 0 for no! |" << endl;
cout << "----------------------------------" << endl;
cin>>trap;

while (trap != "1" && trap != "0") { //You can make this while block into a function if you prefer
cout << "----------------------------------" << endl;
cout << "| That is not a 1 or a 0! |" << endl;
cout << "| Would you like to pick a card? |" << endl;
cout << "| Type 1 for yes or 0 for no! |" << endl;
cout << "----------------------------------" << endl;
cin>>trap;
}
return 0;
}

If you must use an integer then you should look at this and capturing characters without pressing enter.

cin input user for dynamic allocation of array of strings

Something like this

const int MAX_STRING_SIZE = 1024;

int main() {

int numberOfTeams;
char** Teams;

std::cout << "Enter the number of teams " << std::endl;
std::cin >> numberOfTeams;

Teams = new char*[numberOfTeams];
for (int i = 0; i < numberOfTeams; i++) {
Teams[i] = new char[MAX_STRING_SIZE];
std::cin >> Teams[i];
}
for(int i = 0; i < numberOfTeams; ++i) {
delete [] Teams[i];
}
delete [] Teams;

return 0;
}


Related Topics



Leave a reply



Submit