Reputation: 4297
Look at this code:
#include <iostream>
using namespace std;
int main()
{
string s;
int n;
float x;
again:
cout << "Please Type this: ABC456 7.8 9 XYZ\n";
cin >> s >> n >> x >> s;
cout << "\nDo you want to try Again(y/n)? ";
char t;
cin >> t;
if (t=='y' || t=='Y')
goto again;
return 0;
}
Just try to type "ABC456 7.8 9 XYZ" and press enter, it will cause the program exit before prompting the user to try again. I know the inputs are wrong and they are not in their types, but why it causes the exit? and how to avoid such exit?
Upvotes: 6
Views: 3147
Reputation: 20739
Answer https://stackoverflow.com/a/10379322/924727 expalins what happens. About the why, we must go a bit into philosophy.
The C++ stream model is not thought for "human interaction": it is a generic converter of a virtually infinite sequence of characters into a list of space separated "values" to be converted into the supplied "variables".
There is no concept of "input and output interleaving to for a dialog".
If you write your input into a text file like myinput.txt
(unsing correct input)
ABC456 9 7.8 XYZ
Y
ABC456 5 6.7 XYZ
N
and ron your program from the command prompt like
myprogram < myinput.txt
your program will run ... and no "pause" can be required to see the output, since no-one is sitting there to see it and answer it.
The program pauses to wait user input not because of cin >>
, but because cin
is not in fail state and the buffer is empty and the source the buffer remaps is the console. It is the console that waits for '\n' before returning, not cin.
When cin >> n
is called...
operator>>
function is called, and it ...sbumpc
repeatedly to get the digits and compute the number value.All this mechanism makes the fact that - if you type more than required - the buffer content remains available to the next >>
calls, independently it is or not another program line.
A proper "safer" parse requires, afer an input has been read, the stream state to be cleared and the following content to be ignored up to the next '\n'
.
This is typically done with
cin.clear();
cin.ignore(numeric_limits<std::streamsize>::max(), '\n');
So that, whatever had been typed is discarded, and the next cin>>
finds a buffer with no data (just the '\n'
, that is trimmed as "beginning space"), thus causing the console to go in line edit mode again.
Upvotes: 2
Reputation: 153977
Once the stream detects an error, it is in an error state, and all
further attempts to input will be no-ops. Accessing variables read by a
stream without first testing whether the read succeeded is undefined
behavior, so in theory at least, you're program could do anything. (In
practice, if the uninitialized variable is of type char
, all you risk
is a random value.)
When reading line oriented input, the best solution is to use
std::getline
. then use the string that was input to construct an
std::istringstream
to parse the line. This leaves the input stream in
a good state, and already synchronized for the next input. I'd use
something like:
void
getInput()
{
std::string line;
std::cout << "Please type this: ABC456 7.8 9 XYZ" << std::endl;
std::getline( std::cin, line );
if ( ! std::cin ) {
// Very unexpected error...
throw SomethingSerious();
}
std::string s;
int n;
float f;
std::istringstream toParse( line );
toParse >> s >> f >> n >> s;
if ( ! toParse ) {
// Input incorrect...
}
}
bool
tryAgain()
{
std::cout << "Do you want to try again (y/n)? ";
std::string line;
std::getline( std::cin, line );
return line.size() == 1 && (line[0] == 'y' || line[0] == 'Y');
// But I'd be more tolerant with regards to spaces...
}
bool
oneInput()
{
getInput();
return tryAgain();
}
int
main()
{
while ( oneInput() ) {
}
return 0;
}
Upvotes: 1
Reputation: 66
when you call cin.clear(), you should call cin.sync() as the same time.
Upvotes: 1
Reputation: 137870
When the stream extraction operator >>
encounters invalid input, it puts the stream into a mode where no more input is extracted.
You can detect this mode using a Boolean test such as if ( cin )
, and reset it using cin.clear()
. After doing so, the invalid input remains in cin
's input buffer, so you need to process it somehow, such as by ignore
.
Better yet, the extraction operator returns cin
so you can test while you extract:
if ( ! ( cin >> s >> n >> x >> s ) ) {
cout << "You fail horribly!\n";
cin.clear();
cin.ignore( std::numeric_limits< std::streamsize >::max(), '\n' );
}
For more depth see Semantics of flags on basic_ios (and I'm sure there are a few questions on this site that are exact duplicates of this).
Upvotes: 2
Reputation: 61940
Enter a debug line after the cin
cout << "s = " << s << " n = " << n << " x = " << x;
And run
Please Type this: ABC456 7.8 9 XYZ
ABC456 7.8 9 XYZ
s = 9 n = 7 x = 0.8
Clearly the first ABC456 is read into the string s
. The next was a integer therefore only the 7
is read into n
and the 0.8
part was read into float
x
. Now the next input 9
was assigned to s
again therefore the final value of s
is the string "9". And now the first character of X
gets fed into the next cin
where it gets assigned to t
. To confirm just insert another debug line cout << "\nt = " << t;
after you input t
. Therefore the if
is false with the value of t
assigned to 'X' therefore the program exits.
If you input ABC456 7.8 9 YZ
instead the program will ask for input another time, as now t
will have 'Y'.
Upvotes: 3
Reputation: 455282
Change
cin >> s >> n >> x >> s;
to
cin >> s >> x >> n >> s;
As you are entering 7.8
as 2nd input but you are collecting it in a integer variable instead of a floating point variable. As a result of this when you enter:
ABC456 7.8 9 XYZ
s
gets ABC456
, n
gets 7
(as it is of int type and the input buffer still has .8 9 XYZ\n
in it). Next n
gets .8
and finally s
gets "9"
. Now the input buffer has XYZ\n
in it. The next time when you read the input into t
to get the user's choice, X
gets read into t
and since it is not y
or Y
, the loop exits.
Upvotes: 7
Reputation: 1783
The program hits a problem or exception when it tries to but the wrong data types together. Perhaps you want to look at the documentation for the >> operator on cin, look for specifics on what it does when it hits wrong input for data types, and look over the cin>> line and your input for any places that this might be occurring so you can validate the input is handled correctly
Upvotes: 1