By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
459,458 Members | 1,278 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 459,458 IT Pros & Developers. It's quick & easy.

Why doesn't std::cin choke on this?

P: n/a
Hello, when I was writing a user-driven test program for a data structure I
wrote, I encountered an annoying problem. The test program is laid out as a
menu with several numbered options. The user selects one option by typing in
its corresponding number (int). Depending on the choice he made, he may be
asked to provide another integer (or no input at all). So all the user does
is entering integers. I don't want the user to be able to cause problems or
crashes by providing invalid input, but he is despite my error checking.
Here follows output from my program here boiled down to a "minimum":
$ ./test
1 - choice one
2 - choice two
3 - exit program
What is your choice? 1
Enter integer to be doubled: 2+2
2 doubled is 4
1 - choice one
2 - choice two
3 - exit program
What is your choice? Enter integer to be squared:

In this test run the user presses 1 when asked for which choice he wants in
the menu. But when asked to input an integer he enters 2+2, and cin does not
go into an error state but leaves the last '2' plus a newline in the stream,
triggering a new menu option (the '+' was eaten by a call to cin.get() which
is supposed to get rid of the newline. Now how can I fix my code so it
doesn't allow this? And why doesn't cin choke on 2+2 when asked to enter an
integer?

#include <iostream>
#include <limits>
#include <string>

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

int main()
{
std::string prompt;

while(true)
{
std::cout << "1 - choice one" << std::endl;
std::cout << "2 - choice two" << std::endl;
std::cout << "3 - exit program" << std::endl;

int n = enter_integer("What is your choice? ");

switch(n)
{
case 1:
n = enter_integer("Enter integer to be doubled: ");

std::cout << n << " doubled is " << n * 2 << std::endl;

break;
case 2:
n = enter_integer("Enter integer to be squared: ");

std::cout << n << " squared is " << n * n << std::endl;

break;
case 3:
return 0;
default:
std::cout << n << " is an invalid menu choice." << std::endl;
}
}
}

int enter_integer(const std::string& prompt)
{
while(true)
{
std::cout << prompt << std::flush;

int n;

std::cin >> n;

if(!std::cin)
{
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsiz e>::max(),
'\n');

std::cout << "You must enter an integer!" << std::endl;
}
else
{
std::cin.get(); /* Get rid of newline in the stream. */

return n;
}
}

return -1; /* Should never be reached. */
}

/ William Payne
Jul 22 '05 #1
Share this Question
Share on Google+
10 Replies


P: n/a
William Payne wrote:

Hello, when I was writing a user-driven test program for a data structure I
wrote, I encountered an annoying problem. The test program is laid out as a
menu with several numbered options. The user selects one option by typing in
its corresponding number (int).
And this is your problem. You read an int.
The simplest thing you can do is to forget the idea of reading an int.
After all, if the user types 2, this is a character just like 'a' or 'b'.
So read everything as character, it simpler then checking the stream for it's
state, getting rid of the invalid input, clearing that error state and thus
make the stream workable again.
Depending on the choice he made, he may be
asked to provide another integer (or no input at all).
Having no input at all is tricky when you try to read an int. Again: If you
read in a string, this becomes trivial.
So all the user does
is entering integers. I don't want the user to be able to cause problems or
crashes by providing invalid input, but he is despite my error checking.
Here follows output from my program here boiled down to a "minimum":
$ ./test
1 - choice one
2 - choice two
3 - exit program
What is your choice? 1
Enter integer to be doubled: 2+2
2 doubled is 4
1 - choice one
2 - choice two
3 - exit program
What is your choice? Enter integer to be squared:

In this test run the user presses 1 when asked for which choice he wants in
the menu. But when asked to input an integer he enters 2+2, and cin does not
go into an error state
why should it?
Streams are defined that they try to read as much as possible to fullfil the
request. The request was to read an int, thus everything that can be an int
is taken from the stream, in your case the first '2'. The rest is left in the
input queue and is waiting for your program to read it.
but leaves the last '2' plus a newline in the stream,
triggering a new menu option (the '+' was eaten by a call to cin.get() which
is supposed to get rid of the newline. Now how can I fix my code so it
doesn't allow this?
look up the ignore() function of the stream.
But again, it's simpler to *not* read the users input as an int. Read it as a string,
then try to extract the number from the string. This way the user can enter whetever
he likes (even no 'numer' at all), and nothing special will happen: The users input
is read as string and your number detection function will not be able to extract
a number from that and thus your program will emit an error. But in no case the
stream has entered a fail state and must be made useable again (for extracting
something from a string, the stringstreams are a handy tool).
And why doesn't cin choke on 2+2 when asked to enter an
integer?


See above. It's defined to be that way. If you had entered: '*2' it would
have choked, because '*' is not a valid starting character for an integer.

--
Karl Heinz Buchegger
kb******@gascad.at
Jul 22 '05 #2

P: n/a
"William Payne" <mi******************@student.liu.se> wrote...
Hello, when I was writing a user-driven test program for a data structure I wrote, I encountered an annoying problem. The test program is laid out as a menu with several numbered options. The user selects one option by typing in its corresponding number (int). Depending on the choice he made, he may be
asked to provide another integer (or no input at all). So all the user does is entering integers. I don't want the user to be able to cause problems or crashes by providing invalid input, but he is despite my error checking.
Here follows output from my program here boiled down to a "minimum":
$ ./test
1 - choice one
2 - choice two
Whoever designed this user interface should be shot. Why not write

1 - to double an integer
2 - to square an integer

?
3 - exit program
What is your choice? 1
Enter integer to be doubled: 2+2
2 doubled is 4
1 - choice one
2 - choice two
3 - exit program
What is your choice? Enter integer to be squared:

In this test run the user presses 1 when asked for which choice he wants in the menu. But when asked to input an integer he enters 2+2, and cin does not go into an error state but leaves the last '2' plus a newline in the stream, triggering a new menu option (the '+' was eaten by a call to cin.get() which is supposed to get rid of the newline. Now how can I fix my code so it
doesn't allow this? And why doesn't cin choke on 2+2 when asked to enter an integer?


Read the FAQ section 15. 15.2 and 15.3 apply, IMO.

Victor
Jul 22 '05 #3

P: n/a
Karl Heinz Buchegger wrote:

William Payne wrote:

Hello, when I was writing a user-driven test program for a data structure I
wrote, I encountered an annoying problem. The test program is laid out as a
menu with several numbered options. The user selects one option by typing in
its corresponding number (int).
And this is your problem. You read an int.
The simplest thing you can do is to forget the idea of reading an int.
After all, if the user types 2, this is a character just like 'a' or 'b'.
So read everything as character,


Sorry, make that: as string. This also accounts for your user to enter 'abcdefg'
in response to 'Enter a number'.
it simpler then checking the stream for it's
state, getting rid of the invalid input, clearing that error state and thus
make the stream workable again.
Depending on the choice he made, he may be
asked to provide another integer (or no input at all).


Having no input at all is tricky when you try to read an int. Again: If you
read in a string, this becomes trivial.
So all the user does
is entering integers. I don't want the user to be able to cause problems or
crashes by providing invalid input, but he is despite my error checking.
Here follows output from my program here boiled down to a "minimum":
$ ./test
1 - choice one
2 - choice two
3 - exit program
What is your choice? 1
Enter integer to be doubled: 2+2
2 doubled is 4
1 - choice one
2 - choice two
3 - exit program
What is your choice? Enter integer to be squared:

In this test run the user presses 1 when asked for which choice he wants in
the menu. But when asked to input an integer he enters 2+2, and cin does not
go into an error state


why should it?
Streams are defined that they try to read as much as possible to fullfil the
request. The request was to read an int, thus everything that can be an int
is taken from the stream, in your case the first '2'. The rest is left in the
input queue and is waiting for your program to read it.
but leaves the last '2' plus a newline in the stream,
triggering a new menu option (the '+' was eaten by a call to cin.get() which
is supposed to get rid of the newline. Now how can I fix my code so it
doesn't allow this?


look up the ignore() function of the stream.
But again, it's simpler to *not* read the users input as an int. Read it as a string,
then try to extract the number from the string. This way the user can enter whetever
he likes (even no 'numer' at all), and nothing special will happen: The users input
is read as string and your number detection function will not be able to extract
a number from that and thus your program will emit an error. But in no case the
stream has entered a fail state and must be made useable again (for extracting
something from a string, the stringstreams are a handy tool).
And why doesn't cin choke on 2+2 when asked to enter an
integer?


See above. It's defined to be that way. If you had entered: '*2' it would
have choked, because '*' is not a valid starting character for an integer.


--
Karl Heinz Buchegger
kb******@gascad.at
Jul 22 '05 #4

P: n/a

"Victor Bazarov" <v.********@comAcast.net> wrote in message
news:DofKb.64288$I07.277140@attbi_s53...
"William Payne" <mi******************@student.liu.se> wrote...
[snip]
1 - choice one
2 - choice two


Whoever designed this user interface should be shot. Why not write

1 - to double an integer
2 - to square an integer

?


[snip]

LOL, I wrote that and I don't know what I was thinking. The real program has
a menu laid out as you want, though.

[snip]
Read the FAQ section 15. 15.2 and 15.3 apply, IMO.

Victor


I read those sections as you suggested, and I have decided to store the
initial input in a std::string rather than an int and perform error checking
on the string (and converting to int if valid).

Thanks for your reply.

/ William Payne
Jul 22 '05 #5

P: n/a

"Karl Heinz Buchegger" <kb******@gascad.at> wrote in message
news:3F***************@gascad.at...
Karl Heinz Buchegger wrote:

William Payne wrote:

Hello, when I was writing a user-driven test program for a data structure I wrote, I encountered an annoying problem. The test program is laid out as a menu with several numbered options. The user selects one option by typing in its corresponding number (int).
And this is your problem. You read an int.
The simplest thing you can do is to forget the idea of reading an int.
After all, if the user types 2, this is a character just like 'a' or 'b'. So read everything as character,


Sorry, make that: as string. This also accounts for your user to enter

'abcdefg' in response to 'Enter a number'.
it simpler then checking the stream for it's
state, getting rid of the invalid input, clearing that error state and thus make the stream workable again.
Depending on the choice he made, he may be
asked to provide another integer (or no input at all).


Having no input at all is tricky when you try to read an int. Again: If you read in a string, this becomes trivial.
So all the user does
is entering integers. I don't want the user to be able to cause problems or crashes by providing invalid input, but he is despite my error checking. Here follows output from my program here boiled down to a "minimum":
$ ./test
1 - choice one
2 - choice two
3 - exit program
What is your choice? 1
Enter integer to be doubled: 2+2
2 doubled is 4
1 - choice one
2 - choice two
3 - exit program
What is your choice? Enter integer to be squared:

In this test run the user presses 1 when asked for which choice he wants in the menu. But when asked to input an integer he enters 2+2, and cin does not go into an error state


why should it?
Streams are defined that they try to read as much as possible to fullfil the request. The request was to read an int, thus everything that can be an int is taken from the stream, in your case the first '2'. The rest is left in the input queue and is waiting for your program to read it.
but leaves the last '2' plus a newline in the stream,
triggering a new menu option (the '+' was eaten by a call to cin.get() which is supposed to get rid of the newline. Now how can I fix my code so it
doesn't allow this?


look up the ignore() function of the stream.
But again, it's simpler to *not* read the users input as an int. Read it as a string, then try to extract the number from the string. This way the user can enter whetever he likes (even no 'numer' at all), and nothing special will happen: The users input is read as string and your number detection function will not be able to extract a number from that and thus your program will emit an error. But in no case the stream has entered a fail state and must be made useable again (for extracting something from a string, the stringstreams are a handy tool).
And why doesn't cin choke on 2+2 when asked to enter an
integer?


See above. It's defined to be that way. If you had entered: '*2' it would have choked, because '*' is not a valid starting character for an integer.


--
Karl Heinz Buchegger
kb******@gascad.at


Thanks for your reply, Karl. I will do as you suggested and read the user
input into a std::string.

/ William Payne
Jul 22 '05 #6

P: n/a

"William Payne" <mi******************@student.liu.se> wrote in message
news:bt**********@news.island.liu.se...

"Karl Heinz Buchegger" <kb******@gascad.at> wrote in message
news:3F***************@gascad.at...
Karl Heinz Buchegger wrote:

William Payne wrote:
>
> Hello, when I was writing a user-driven test program for a data structure I > wrote, I encountered an annoying problem. The test program is laid out
as a
> menu with several numbered options. The user selects one option by typing in > its corresponding number (int).

And this is your problem. You read an int.
The simplest thing you can do is to forget the idea of reading an int.
After all, if the user types 2, this is a character just like 'a' or 'b'. So read everything as character,
Sorry, make that: as string. This also accounts for your user to enter

'abcdefg'
in response to 'Enter a number'.
it simpler then checking the stream for it's
state, getting rid of the invalid input, clearing that error state and thus make the stream workable again.

> Depending on the choice he made, he may be
> asked to provide another integer (or no input at all).

Having no input at all is tricky when you try to read an int. Again:
If you read in a string, this becomes trivial.

> So all the user does
> is entering integers. I don't want the user to be able to cause problems or > crashes by providing invalid input, but he is despite my error checking. > Here follows output from my program here boiled down to a "minimum":
> $ ./test
> 1 - choice one
> 2 - choice two
> 3 - exit program
> What is your choice? 1
> Enter integer to be doubled: 2+2
> 2 doubled is 4
> 1 - choice one
> 2 - choice two
> 3 - exit program
> What is your choice? Enter integer to be squared:
>
> In this test run the user presses 1 when asked for which choice he wants in > the menu. But when asked to input an integer he enters 2+2, and cin does not > go into an error state

why should it?
Streams are defined that they try to read as much as possible to
fullfil
the request. The request was to read an int, thus everything that can be
an
int is taken from the stream, in your case the first '2'. The rest is left in the input queue and is waiting for your program to read it.

> but leaves the last '2' plus a newline in the stream,
> triggering a new menu option (the '+' was eaten by a call to
cin.get()
which > is supposed to get rid of the newline. Now how can I fix my code so
it > doesn't allow this?

look up the ignore() function of the stream.
But again, it's simpler to *not* read the users input as an int. Read
it as a string, then try to extract the number from the string. This way the user can enter whetever he likes (even no 'numer' at all), and nothing special will happen:
The
users input is read as string and your number detection function will not be able
to
extract a number from that and thus your program will emit an error. But in no case the stream has entered a fail state and must be made useable again (for extracting something from a string, the stringstreams are a handy tool).

> And why doesn't cin choke on 2+2 when asked to enter an
> integer?

See above. It's defined to be that way. If you had entered: '*2' it would have choked, because '*' is not a valid starting character for an integer.


--
Karl Heinz Buchegger
kb******@gascad.at


Thanks for your reply, Karl. I will do as you suggested and read the user
input into a std::string.

/ William Payne


I have now written a small code snippet that prompts the user to enter an
integer, a snippet which should reject any input that is not a valid
integer. It seems to work but I would like to hear some comments on it if
you please.

int enter_integer(const std::string& prompt)
{
while(true)
{
std::cout << prompt << std::flush;

int n;

std::string input;

/* Maybe use cin.getline() instead and complain if the user *
* enters, for example, 2 2, instead of just silently dis- *
* everything left in stream like we do now? */
std::cin >> input;

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

if(!check_if_valid_integer(input))
{
std::cout << input << " is not a valid integer." << std::endl;
}
else
{
std::stringstream ss;

ss << input;

/* check_if_valid_integer() doesn't catch cases where the user just
*
* enters '+' or '-', so we need this check here. Maybe we should
*
* check for that in check_if_valid_integer as well.
*/
if((ss >> n))
{
return n;
}
else
{
std::cout << input << " is not a valid integer." << std::endl;
}
}
}

return -1; /* Should never be reached. Maybe throw exception? */
}

bool check_if_valid_integer(const std::string& s)
{
if(!isdigit(s[0]) && s[0] != '+' && s[0] != '-')
{
return false;
}

return count_if(s.begin() + 1, s.end(), isdigit) == 0;
}

Hope this post doesn't go unnoticed, since I decided to reply to this thread
instead of starting a new one.

/ William Payne
Jul 22 '05 #7

P: n/a

"William Payne" <mi******************@student.liu.se> wrote in message
news:bt**********@news.island.liu.se...
Hello, when I was writing a user-driven test program for a data structure I wrote, I encountered an annoying problem. The test program is laid out as a menu with several numbered options. The user selects one option by typing in its corresponding number (int). Depending on the choice he made, he may be
asked to provide another integer (or no input at all). So all the user does is entering integers. I don't want the user to be able to cause problems or crashes by providing invalid input, but he is despite my error checking.
Here follows output from my program here boiled down to a "minimum":
$ ./test
1 - choice one
2 - choice two
3 - exit program
What is your choice? 1
Enter integer to be doubled: 2+2
2 doubled is 4
1 - choice one
2 - choice two
3 - exit program
What is your choice? Enter integer to be squared:

In this test run the user presses 1 when asked for which choice he wants in the menu. But when asked to input an integer he enters 2+2, and cin does not go into an error state but leaves the last '2' plus a newline in the stream, triggering a new menu option (the '+' was eaten by a call to cin.get() which is supposed to get rid of the newline. Now how can I fix my code so it
doesn't allow this? And why doesn't cin choke on 2+2 when asked to enter an integer?


Other replies have pointed out why it doesn't work but I think you will find
that the right answer is not to use a string or at least
not as in cin >> s.

All programs of this type are line oriented and hence you need to read a
line -
1. use the function getline to read a whole line into a std::string.
2. Make your line the basis of an istringstream, iss
3. read your integer with if( iss >> i)
4. skip whitespace with iss >> std::ws
5. if iss.get() is not now EOF then there was some input you didn't ask for

This does the biz without you explicitly parsing strings and also stops the
user entering extra newlines (remember that all formatted input functions
eat whitespace first and newlines are whitespace).
Jul 22 '05 #8

P: n/a

"William Payne" <mi******************@student.liu.se> wrote in message
news:bt**********@news.island.liu.se...

[Please snip out any text not relevant to your reply, thanks]

| I have now written a small code snippet that prompts the user to enter an
| integer, a snippet which should reject any input that is not a valid
| integer. It seems to work but I would like to hear some comments on it if
| you please.

[snip]

| Hope this post doesn't go unnoticed, since I decided to reply to this thread
| instead of starting a new one.

Oh, it was noticed alright :-).

Having said that, I have modified your code a little:

bool check_if_valid_integer( const std::string& s )
{
static const char* ValidChars( "0123456789" );

return s.find_first_not_of( ValidChars ) == std::string::npos;
}

int enter_integer( const std::string& prompt )
{
while( true )
{
std::cout << prompt << std::flush;

int n;

std::string input;
std::getline( std::cin, input );

if( !check_if_valid_integer( input ) )
{
std::cout << input << " is not a valid integer." << std::endl;
return -1;
// Note: '-1' is a valid integer, it is up to you if you
// want to accept it - throwing an exception may be of
// better here.
}
else
{
std::istringstream ss( input );

if( ss >> n )
return n;
}
}
}

Cheers.
Chris Val
Jul 22 '05 #9

P: n/a
<snip>
bool check_if_valid_integer( const std::string& s )
{
static const char* ValidChars( "0123456789" );

return s.find_first_not_of( ValidChars ) == std::string::npos;
}


wouldn't this be better named check_if_valid_unsigned_integer() ?
To check for valid integer, you need to allow '-' but only in the first postion
no? Why not just use strstream functions?

Jul 22 '05 #10

P: n/a

"Duane Hebert" <sp**@flarn2.com> wrote in message
news:pb*********************@weber.videotron.net.. .
| <snip>
| > bool check_if_valid_integer( const std::string& s )
| > {
| > static const char* ValidChars( "0123456789" );
| >
| > return s.find_first_not_of( ValidChars ) == std::string::npos;
| > }
|
| wouldn't this be better named check_if_valid_unsigned_integer() ?
| To check for valid integer, you need to allow '-' but only in the first postion
| no? Why not just use strstream functions?

Your right(but I didn't want to change it), that's why
I mentioned it, but still allowed it to return a negative
value for the error code.

I should have been a little more clear :-).

Cheers.
Chris Val
Jul 22 '05 #11

This discussion thread is closed

Replies have been disabled for this discussion.