How to Make Cin Take Only Numbers

How to make cin take only numbers

I would use std::getline and std::string to read the whole line and then only break out of the loop when you can convert the entire line to a double.

#include <string>
#include <sstream>

int main()
{
std::string line;
double d;
while (std::getline(std::cin, line))
{
std::stringstream ss(line);
if (ss >> d)
{
if (ss.eof())
{ // Success
break;
}
}
std::cout << "Error!" << std::endl;
}
std::cout << "Finally: " << d << std::endl;
}

C++: Just allow numbers as an input

When you declare a type of a variable, the variable can't contain anything else than what you have declared. So: You can’t use an int m to input a float. You could however use cin.ignore() (more details here) to accept a user input of "4.1" as "4". Here you go:

#include <iostream>
#include <limits>

using namespace std;

int main() {
cout << "Enter an int: ";

int m = 0;
while(!(cin >> m)) {
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
cout << "Invalid input!\nEnter an int: ";
}

cout << "You enterd: " << m << endl;
}

how to make cin only accept a single character and int accept only numbers?

By definition, operator>> reading into a char will read in exactly 1 character, leaving any remaining input in the buffer for subsequent reading. You have to validate the read is successful and the character is what you are expecting before using it.

And likewise, operator>> reading into an int will read in only numbers. You have to validate the read is successful before using the number, and discard any unused input if the read fails to return a valid number.

Try something like this:

#include <iostream>
#include <string>
#include <limits>
using namespace std;

char ReadChar(const char* prompt) {
string s;
do {
cout << prompt << ": ";
if (!(cin >> s)) throw ...;
if (s.length() == 1) break;
//cin.ignore(numeric_limits<streamsize>::max(), '\n');
cout << "Invalid input. Enter a single character" << endl;
}
while (true);
return s[0];
}

char ReadInt(const char* prompt) {
int value;
do {
cout << prompt << ": ";
if (cin >> value) break;
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
cout << "Invalid input. Enter a valid number" << endl;
}
while (true);
return value;
}

int main() {
char opt;
int num1, num2, result;
cout << "A. Addition" << endl << "B. Subtraction" << endl;
opt = ReadChar("Enter option");
switch (opt) {
case 'A': case 'a': {
num1 = ReadInt("Enter first number");
num2 = ReadInt("Enter second number");
result = num1 + num2;
cout << "The sum is " << result << endl;
break;
}
case 'B': case 'b': {
num1 = ReadInt("Enter first number");
num2 = ReadInt("Enter second number");
result = num1 - num2;
cout << "The difference is " << result << endl;
break;
}
default: {
cout << "Invalid option" << endl;
break;
}
}
}

C++ cin only accept numeric values

Below is a method based on stuff I read in one of the early chapters of Stroustrup's Programming: Principles and Practice Using C++ and an answer provided by Duoas at cplusplus.com. It defines a function, get_int_between(), that allows you to do something like this:

int my_variable;
get_int_between(my_variable, min, max, prompt, error_msg);

Which would prompt, validate, and store into my_variable.

Just for fun, I've also included a function, get_int(my_variable, prompt, error_msg), that does the same thing but allows an integer of any value.

#include <iostream>
#include <sstream> // stringstream

void get_int(int& d, std::string prompt, std::string fail);

void get_int_between(int& d, int min, int max, std::string prompt, std::string fail);

int main()
{
int my_number = 1; // initialize my_number

get_int(my_number, "Please enter an integer: ", "Sorry, that's not an integer.\n");
//Do something, e.g.
std::cout << "You entered: " << my_number << "\n";

get_int_between(my_number, 1, 2, "Choose the game type (1 or 2): ", "Sorry, that's not an integer.\n");
//Do something, e.g.:
std::cout << "Let's play Game " << my_number << "!\n";

return 0;
}

void get_int(int& d, std::string prompt, std::string fail)
{
while(1) {

std::cout << prompt;
std::string str;
std::cin >> str;

std::istringstream ss(str);
int val1;
ss >> val1;

if(!ss.eof()) {
std::cout << fail;
continue;
} else {
d = val1;
break;
}
}
}

void get_int_between(int& d, int min, int max, std::string prompt, std::string fail)
{
while(1) {
get_int(d, prompt, fail);
if(d > max || d < min) {
std::cout << "Sorry, your choice is out of range.\n";
continue;
}
break;
}
}

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.

Only accepting numeric input from user

So, is there any method that only accept pure numeric and also include decimal point?

The function std::strspn will return the length of the string that contains only characters that are found in another string. So, if you want to allow both decimal digits and decimal points, you could use the following for input validation:

bool isNumber( string feet )
{
return strspn( feet.c_str(), "0123456789." ) == feet.length();
}

However, as already pointed out in the comments section, the conversion function std::stod itself provides information that can be used for input validation. If you provide a second parameter to that function, it will write to the provided variable the number of characters that were matched.

The function std::stod automatically consumes all leading whitespace characters (e.g. space and tab characters) before converting the value. If you also want to also allow trailing whitespace characters, but no other types of characters, then you may want to use the following code:

std::size pos;
double d;

//attempt the conversion
d = stod( feet, &pos );

//make sure that at least one character was converted
if ( pos == 0 )
throw "Input was invalid!";

//make sure that all remaining characters are whitespace
while ( feet[pos] != '\0' )
{
if ( !isspace( (unsigned char)feet[pos] ) )
throw "Input was invalid!";
pos++;
}

If you don't want to allow any whitespace at all, including leading whitespace, then you will have to validate the string contents yourself before passing it to std::stod.

You are probably wondering why I am casting the char to unsigned char. See this question for an explanation of the reason why this is necessary. For the same reason, the following line in your code is wrong:

if (isdigit(feet[i]) == false){

It should be changed to:

if (isdigit((unsigned char)feet[i]) == false){

Also, the following problems in your code seem worth mentioning:


The line

cin >> feet;

will read a single word of input. So if the user enters for example 2318 sijslnej, then that line of code will only write 2318 into feet. I doubt that this is what you want. You may want to use feet.getline instead, as that will read the entire line instead of only the first word.


I strongly suggest that you get out of the habit of writing this:

if ( isdigit(...) == false )

Although this line will always work, it is still a very bad habit, as this habit will also cause you to write the following:

if ( isdigit(...) == true )

This line is equivalent to

if ( isdigit(...) == 1 )

which is wrong. The function std::isdigit returns a value of type int, not bool, and the return value is not guaranteed to be 0 or 1. It can return any nonzero value to indicate a digit, for example 20. In that case, the if conditional expression mentioned above will be equivalent to 20 == 1, which will evaluate to false. This is not what you want.

Therefore, instead of

if ( isdigit(...) == false )

you should write

if ( !isdigit(...) )

and instead of

if ( isdigit(...) == true )

you should write:

if ( isdigit(...) )



Related Topics



Leave a reply



Submit