473,396 Members | 1,774 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,396 software developers and data experts.

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

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
10 1960
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
"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
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

"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

"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

"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

"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

"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
<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

"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 thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

5
by: Chris Mantoulidis | last post by:
Let's say I have this: std::string s1; std::cin >> s1; This will read s1 from cin until it finds a space (or a newline, whichever comes first). Okay this works. But when I want to continue...
3
by: yw | last post by:
Hi, When I use std::cin and std::cout to input some data, how can I ingore the cin by using the Enter key? For example: string sname; cout<<"Please input your name:"<<endl; cin>>sname; If...
3
by: puzzlecracker | last post by:
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); I have seen it in faq - what does it do, exactly?
3
by: moleskyca1 | last post by:
In C++ FAQ (15.5), I see this code: int i = 0; while (std::cin >x) { // RIGHT! (reliable) ++i; // Work with x ... } but I don't know how can while loop end? ostream &operator >>(...) return...
8
by: junw2000 | last post by:
Below is simple code: #include <iostream> int main(){ int i; while(1){ while(!(std::cin >i)){ std::cout<<"Invalid input i: "<<i<<'\n';
8
by: Johannes Meng | last post by:
Good day, I'm experimenting with unbuffered input at the moment. To get input I basically use cin.get(). My problem are control sequences preceeded by an ESC character (i.e. up, down, f-keys et...
12
by: pekka | last post by:
I'm trying to measure user input time with my Timer class object. It isn't as easy as I expected. When using std::cin between timer start and stop, I get zero elapsed time. For some unknown reason,...
3
by: Alex Snast | last post by:
hello guys I need to modify the std::cin delim char from the default ' ' and '\n' characters to ',' i know that i can edit the delim in the getline command however i'd like to know if there's...
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.