How do I force users to input integers only in my program?

Pages: 12
Hey I'm really new to C++ and I'm need some help!
What is an efficient way to force users to enter only integers within a certain range(e.g. 1 to 10)?

1
2
3
4
5
6
7
int myFace;
cout << "Please rate my face from 1 to 10"
cin>> myFace;
if (myFace >7)
{
   cout << "I am handsome";
}


How do I force users to enter integers within 1 to 10 only? It seems like with programs like this if I enter an alphabet instead of number it screws up the whole output...
Last edited on
Write a function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <ciso646>
#include <iostream>
#include <sstream>
#include <string>

std::istream& getline( std::istream& ins, int& n )
{
  n = 0;  // stupid C++11 standard behavior

  // Read a line (terminated by ENTER|NEWLINE) from the user
  std::string s;
  if (std::getline( ins, s ))
  {
    // Get rid of any trailing whitespace
    s.erase( s.find_last_not_of( " \f\n\r\t\v" ) + 1 );

    // Convert it to integer
    std::istringstream ss( s );
    ss >> n;

    // Check to see that there is nothing left over
    if (!ss.eof())
      ins.setstate( std::ios::failbit );
  }
  return ins;
}

int main()
{
  using namespace std;
  int myFace;
  cout << "Please rate my face from 1 to 10: ";

  while (!getline( cin, myFace ) or (myFace < 1) or (myFace > 10))
  {
    cin.clear();
    cout << "Try again: ";
  }

  if (myFace > 7) cout << "Thanks!\n";
  else            cout << "Whatever, dude.\n";
}

Hope this helps.
@ Duoas He's a beginner for God's sake..
(ps - dont kill me for my solution please.. )

@OP

Try this:

1
2
3
4
5
6
7
8
9
char myFace;
cout << "\n Please rate my face from 1 to 10";
cin>> myFace;
if ((myFace >'7')&&(myFace<='10'))
   cout << "\n I am handsome";
else if ((myFace>='1')&&(myFace<='7'))  
   cout<<"\n Whatever, dude. ";
else 
   cout<<"\n Invalid Choice. ";
Last edited on
damn this was much harder than I thought! Thanks man!
@Pratik K
Sigh. Another 'too much for a beginner' argument.

Is it too much for a beginner to answer his question? Or to do things the correct way?

The code you posted is incorrect (line 4), and its handling of the user's input is significantly more complicated than what the OP posted (why all the crazy conditions now just to see if myFace > 7 or not?), and it no longer receives integer input.

I know the function looks a little scary, but only because it isn't a one-line answer. Even if OP doesn't understand everything going on inside the function, it is clearly commented and easy to use.

Any beginner can just cut and paste the magic function I posted and then live in happy land with the very simple example I gave on lines 34 through 38.

And by the time he gets to wanting to quantify the user's input as good or bad, there is no invalid choice.

That is good user input handling.
@Pratik K

Sorry dude, but a char can hold only 1 char :+) On line 4, you are trying to cram 2 into 1 ! Would be al-right if it only went up to 9.

Also, if a string was input, cin would fail on line 3 .....

Wasn't trying to kill you for your solution - more of a poke in the ribs :+)
@ @TheIdeasMan

Firstly, cin works. I've tried it out. And secondly can someone explain how line 3 is wrong? I mean, the if statements are equivalent to comparing the ASCIIs of said char and what the user has entered, so I don't see how it would be a problem?
@duoas
Instead of going into stuff that's probably going to pass over their head, wouldn't it be better to solve the problem with stuff they already know? Even if it means the code may be a but complicated in terms of understanding, bit at least it can be followed right? I mean the purpose here is not only to help people solve the problem, but to present the solution in a way that would help them understand?
Last edited on
you've got '10' which is not one character.
http://stackoverflow.com/questions/7459939/what-do-single-quotes-do-in-c-when-used-on-multiple-characters


> n = 0; // stupid C++11 standard behavior
¿what's your complain?
iirc, c++11 says that an invalid reading operation on a basic type will set it to 0.


1
2
    // Get rid of any trailing whitespace
    s.erase( s.find_last_not_of( " \f\n\r\t\v" ) + 1 );
¿couldn't ss>>n>>std::ws; be used?
Okay, I'll bite.

Firstly, cin works.
Of course cin works. C++ language features work just fine.

We don't have a problem with cin. We have a problem with your code, which doesn't work:

I've tried it out.
I doubt it. Because if you had you would have gotten an error message that looked something like this:
a.cpp:9:29: warning: multi-character character constant [-Wmultichar]
 if ((myFace >'7')&&(myFace<='10'))
                             ^
And secondly can someone explain how line 3 is wrong? I mean, the if statements are equivalent to comparing the ASCIIs of said char and what the user has entered, so I don't see how it would be a problem?
Besides the fact that '10' is not the single character you seem to think it is, you are trying to equate "10" and '10', which are two totally different things.

But the greater problem is that you have changed the meaning of OP's original code:
1
2
3
4
int myFace;
cout << "Please rate my face from 1 to 10"
cin>> myFace;
if (myFace >7)
myFace is an integer (with multi-digit range)

input an integer (convert multiple characters to an integer value)
compare integers

Your code:
1
2
3
4
char myFace;
cout << "\n Please rate my face from 1 to 10";
cin>> myFace;
if ((myFace >'7')&&(myFace<='10'))
myFace is a character (with single-digit range)

input a single character (no conversion done -- ignores all but first digit)
compare characters

Your solution only works for input strings in the range ["0", "9"].
OP's program works for any input string that can be converted to an integer in any subrange of [INT_MIN, INT_MAX].
That's a pretty significant difference, especially as OP's code specifically calls for input in the range "1" to "10" -- which your code cannot handle properly, even though it claims it does.
And now to pick a bone.
@duoas
Instead of going into stuff that's probably going to pass over their head, wouldn't it be better to solve the problem with stuff they already know? Even if it means the code may be a but complicated in terms of understanding, bit at least it can be followed right? I mean the purpose here is not only to help people solve the problem, but to present the solution in a way that would help them understand?
So.... you've got the gall to use my own argument against me.

Let's see which one of us is using it fallaciously.


OP's code
1
2
3
4
5
6
7
int myFace;
cout << "Please rate my face from 1 to 10"
cin>> myFace;
if (myFace >7)
{
   cout << "I am handsome";
}
I want an integer
in the range 1..10
Get the integer*
Compare the integer to 7

*OP specifically states that he wants to:
  - Constrain the input to the range 1..10
  - Constrain the input to valid numbers (because things go wonky when the user inputs anything but numbers)
Proof is what lune wrote:
How do I force users to enter integers within 1 to 10 only? It seems like with programs like this if I enter an alphabet instead of number it screws up the whole output...


Duoas's code
1
2
3
std::istream& getline( std::istream& ins, int& n )
{
}
Small block of code containing a magic function to 
read an integer from input. It is heavily commented, 
but completely understanding it exactly is optional.
1
2
3
4
5
6
7
8
9
10
  int myFace;
  cout << "Please rate my face from 1 to 10: ";

  while (!getline( cin, myFace ) or (myFace < 1) or (myFace > 10))
  {
    cin.clear();
    cout << "Try again: ";
  }

  if (myFace > 7) 
I want an integer
in the range 1..10

Get the integer*
(until correct)




Compare the integer to 7

*Code to get the integer specifically address OP's problems:
  - input must be in range [1,10]
  - input must be an integer (and nothing else)
I don't permit the code to continue past this point until user has satisfied those two conditions. (Hmm, this might be important!)


Pratik's code
1
2
3
4
char myFace;
cout << "\n Please rate my face from 1 to 10";
cin>> myFace;
if ((myFace >'7')&&(myFace<='10'))
I don't want an integer -- I'd rather a character
in the range 1 to 10 (how is '10' a single character?)
Get the character*
Compare the character to '7' and '10'

*Great, we're getting a single character, so:
  - cin won't go wonky on us if the user tries to input a non-number
  - We have constrained the input to single-digit characters, instead of integers in the range 1..10
  - Valid inputs now include 'a' and '$'.

So... not only does it fail to constrain input to integers in the range 1..10, it now accepts any character input, which means that the code that follows has to deal with it:
1
2
3
4
5
6
if ((myFace >'7')&&(myFace<='10'))
   cout << "\n I am handsome";
else if ((myFace>='1')&&(myFace<='7'))  
   cout<<"\n Whatever, dude. ";
else 
   cout<<"\n Invalid Choice. ";
compare to '7' AND to check that input was received in range

still checking that input is in range

deal with input that was not in range


The verdict?
1st wrote:
Instead of going into stuff that's probably going to pass over their head, wouldn't it be better to solve the problem with stuff they already know?
It depends on how you apply it. Here you are applying it with a switch and bait logic.
- All programming involves using black boxes of code. How does cin >> myFace work? Who cares? The beginner can still use it just fine. Input is a difficult topic, and a lot of it will necessarily be opaque to a beginner.
- But a beginner can easily understand something simple like:
    attempt to get input
    make sure it is in range
    if any of those fail, complain to the user and try again
Hmm. Maybe teaching a beginner how to think about getting input is part of the solution?

Duoas: Here's a common idiom that you should learn to get input
Pratik: Let's inexplicably change things to use characters, add in a whole bunch of conditions, and let bad inputs in. (OP's code didn't even do that!)

2nd wrote:
Even if it means the code may be a but complicated in terms of understanding, bit at least it can be followed right?
Something like that nicely-commented function I posted? It doesn't do anything above a beginner's head (plays with strings, uses a stringstream, that failbit thing might be something to figure out some day).
Or, if you treat the function as incomprehensible, at least the part in main() where the function is used is, while a little more complicated than a single cin>>myFace, it can be followed with little effort.

Duoas: Code is commented and follows a clean, easy to read pattern.
Pratik: Code is dense and does not explain why using chars is better than the ints, or why we are now having to deal with 'Invalid Choice's.

3rd wrote:
I mean the purpose here is not only to help people solve the problem, but to present the solution in a way that would help them understand?

Duoas: Code solves both of OP's problems AND does it with an easy to read example of good input handling principles (use a loop, try to get input, match to range, repeat until good)
Pratik: Code fails to solve one of OP's problems AND introduces another problem AND spreads logic out AND changes behavior of the program AND doesn't work as advertised...

Hmm. QED.

I am sorry, but you asked for it. Welcome to being a jerk on the internet.

@lune
I hope you get a good laugh factor out of this, and maybe learn something interesting and useful. In any case, sorry to make you put up with it.

@ne555
¿what's your complain?

I think that assigning a value on input failure is stupid. (But that's my opinion.)

¿couldn't ss>>n>>std::ws; be used?

I hadn't thought of that.
Future incantations will be updated. (But I'm not going to press Edit for anything in this thread.)
Thanks!
Another take -- also using istringstream -- basically lifted and simplified from cire's templated version here:
http://www.cplusplus.com/forum/beginner/108849/#msg592118
thread: Trying to limit input to int type (19 Aug 2013)

Andy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <iostream>
#include <sstream>
#include <string>

int getIntValueFrom1to10() {
    int  result  = 0;
    bool notDone = true;
    while(notDone) {
        std::string line;
        std::getline(std::cin, line);

        std::istringstream is(line);

        char dummy = '\0';
        // if we fail to extract an int
        // or we find something apart from whitespace after the int
        // or the value isn't in range
        if (     !(is >> result)
              ||  (is >> std::ws && is.get(dummy))
              ||  (result < 1) || (result > 10)      )
            std::cout << "Invalid input. Try again!\n";
        else
            notDone = false ;
    }

    return result;
}

int main() {
    std::cout << "Please rate my face from 1 to 10: ";

    int myFace = getIntValueFrom1to10();
    if (myFace > 7)
        std::cout << "Thanks!\n";
    else
        std::cout << "Whatever, dude.\n";

    return 0;
}
Last edited on
@duoas
Hey, listen, I'm firstly really sorry and stupid to not notice the 10, forgot that part so yeah I take full blame and criticism for that.
And yeah what I meant by tested was that I tested the part about the cin as in not specifically the program, just the one part.
Let's agree to disagree on the simplicity part though. Cause I don't see how adding jargon makes the program easier man. I still feel my method (again, apart from the part of "10") it good cause it handles any possible single character entries, doesn't it? And, agreeing my mistake was a mistake, and building on what char-concept how about;:
1
2
3
char a[10];
cin>>a;
//then the if else but with strcmp 

?
I mean can you tell me why is this not an efficient and robust way of enforcing the user to enter only numbers? And even if some guy presses presses like a lot of keys, it would handle the situation.
Also, agreed code is longer and more cumbersome but it does what it's supposed to doesn't it? And that too I feel to robustly, all while ensuring op knows what's going on. (probability of him/her knowing strcmpi and if else stts Is much greater than said op knowing ignore and stuff)

But in the end, I wholly agree that the one mistake was mine and yeah I'm sorry if I confused said op.
But the thing is @duoas, even when I started off on this site, all the complex stuff I came across was undoubtedly correct, accurate and precise. But it really prevented me from going deeper into, rather into more creative ways of developing solutions. Honestly, stuff like that intimidated me as a beginner I ended up copy pasting the solution without a second glance. I just don't want people to suffer the initial feeling of vastness i had when I approached this subject. I really didn't mean to offend you or anything and yeah if I did I'm sorry.
Pratik K wrote:
I mean can you tell me why is this not an efficient and robust way of enforcing the user to enter only numbers?

Using a fixed size buffer and not limiting the amount of characters extracted to that buffer is the opposite of robust.


Pratik K wrote:
And even if some guy presses presses like a lot of keys, it would handle the situation.

It definitely would not handle the situation.
> then the if else but with strcmp
please expand on the idea.
note that "111" would be compared less to "7"
I'm firstly really sorry
Thank you.

Let's agree to disagree on the simplicity part though.
No. You're wrong.

It doesn't get any simpler than what I posted. You are confusing simplicity with character count.

Correct and simple is:
1
2
3
4
5
6
ask user for input
while (!attempt_to_get_input || !input_in_range)
{
  clear input stream
  ask user to try again
}

This is the most efficient way: it takes no steps that are not entirely necessary.
This is the most robust way: it refuses all invalid input.

RE: your overwhelming experience when you started out
That's pretty normal. Learning to do this stuff has a pretty steep learning curve.

Hope this helps.
closed account (48T7M4Gy)
The key to solving all this messy stuff is what Stroustrup says:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
int main etc etc
{
   cout  Please enter a number
   int x = -1
   cin >> x

  if(!std::cin || x < lowerLimit || x > upperLimit )
 {
      cout BLOOPER
 }
else
 {
     cout BINGO
 }
Last edited on
Close, but still wrong.

Rephrase:
1
2
3
4
5
6
7
8
ask user for input
if (!attempt_to_get_input || !input_in_range)
{
  cout BLOOPER
  terminate();
}
cout BINGO
carry on

The loop assumes there is a human.
closed account (48T7M4Gy)
Please explain.
No.

Someone is reporting my attempts to explain, so I'm going away.
There are now two versions: a loop and a one-timer.

The loop keeps asking from a user until it a valid input is given. That is fine with a user, but piping data non-interactively will fail miserably.

The one-timer aborts on error and will not misuse the following data stream. For a batch job this is acceptable; you check and fix the error and resubmit the job. For an interactive user the UI behaviour is not convenient. The user can keep supplying new corrected input for this one question, and once right proceed with the rest of the program. The termination prevents that.

UI design philosophy. There is more than one solution, but some are less intuitive than others.
Pages: 12