Incorrect inputs will cause program output

Take a 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 entering "ABC456 7.8 9 XYZ" and press "Enter", this will exit the program before you ask the user to try again. I know that the input is wrong and they are not in their types, but why does this cause the output? and how to avoid such an exit?

+6
source share
7 answers

Answer fooobar.com/questions/914489 / ... what happens. About why, we need to delve a little into philosophy.

The C ++ stream model is not considered for "human interaction": it is a general transformer of an almost infinite sequence of characters into a list of spatially separated "values" that must be converted to the provided "variables".

There is no concept of "input and output interleaving for dialogue." If you enter your input in a text file, for example myinput.txt (invalid input)

 ABC456 9 7.8 XYZ Y ABC456 5 6.7 XYZ N 

and run the program from the command line, for example

  myprogram < myinput.txt 

your program will start ... and there is no need for a β€œpause” to see the result, since no one is sitting there to see it and answer it.

The program stops to wait for user input not because of cin >> , but because cin not in a failed state, and the buffer is empty, and the console is the source for resetting the buffer. This is a console that waits for "\ n" before returning, not cin.

When cin >> n is called ...

  • function operator>> is called, and it ...
  • gets the num_get face from the stream locale and calls the get function, which ...
  • call the sbumpc stream buffer several times to get the numbers and calculate the value of the number.
  • If the buffer has content, it simply returns its characters one by one. When the character is no longer present (or if it is empty) ...
  • The buffer requests the operating system to read from a low-level file.
  • If the file is a console, the editor of the console internal line is called:
  • This causes the console to linger, allowing the user to press a character and some controls (e.g. backspace, for example) until a key is pressed
  • The console line editor returns the line to the operating system, which will allow the contents of the available input CON file ...
  • This is read by the buffer (after transmitting the read characters to the cvt locale plot, but this is detailed information), which is filled in by itself.
  • Now make yourself return the U-turn.

This whole mechanism is made by the fact that - if you enter more than required, the contents of the buffer remain available for the next calls >> , regardless of whether it is another program or not.

It requires the correct "safe" disassembly, confirmation of input, the status of the stream to be cleaned, and the next content that must be ignored until the next '\n' . This is usually done using

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

Thus, everything that was printed is discarded, and the next cin>> finds the buffer without data (only '\n' , which is truncated as the "initial space"), forcing the console to go into the queue, edit the mode again.

+2
source

Change

 cin >> s >> n >> x >> s; 

to

 cin >> s >> x >> n >> s; 

As you type 7.8 as the second input, but you collect it in an integer variable instead of a floating point variable. As a result of this, when entering:

 ABC456 7.8 9 XYZ 

s gets ABC456 , n gets 7 (like int, and the input buffer still has .8 9 XYZ\n ). The next n gets .8 and finally s gets "9" . Now the input buffer has XYZ\n . The next time you read the input at t to get the user's choice, X read at t , and since it is not y or y , the loop ends.

+7
source

Enter debug line after 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 string s . The following was an integer, so only 7 read in n , and part 0.8 read in float x . Now the next input 9 been assigned s again, so the final value of s is the string "9". And now the first character x will be passed to the next cin , where it will be assigned t . To confirm, just insert another debug line cout << "\nt = " << t; after entering t . Therefore, if is false with a value of t assigned to "X", so the program exits.

If you enter ABC456 7.8 9 YZ , the program will ask for input at another time, since now t will have "Y".

+3
source

When the stream extraction operator >> encounters invalid input, it puts the stream in a mode in which the input is no longer retrieved.

You can detect this mode with a boolean test such as if ( cin ) and reset using cin.clear() . After that, the invalid input remains in the cin input buffer, so you need to somehow handle it, for example, ignore .

Even better, the extract statement returns cin so you can check while you are extracting:

 if ( ! ( cin >> s >> n >> x >> s ) ) { cout << "You fail horribly!\n"; cin.clear(); cin.ignore( std::numeric_limits< std::streamsize >::max(), '\n' ); } 

See the semantics of flags on basic_ios for more details (and I'm sure there are a few questions on this site that are exact duplicates of this).

+2
source

A program gets into a problem or exception when it tries, but not with the same data types. Perhaps you want to look at the documentation for the operator β†’ on cin, look at the features of what it does when it mistakenly enters data for data types, and look at the cin β†’ line and your data for any places that may be so you can check correct input.

+1
source

when you call cin.clear (), you must call cin.sync () at the same time.

+1
source

As soon as the stream detects an error, it is in an error state and all further input attempts will be absent. Access to variables read by the stream without prior testing, whether the following has been read: undefined behavior, so theoretically, at least you can do something. (In practice, if an uninitialized variable is of type char , all you risk is a random value.)

When reading linear oriented input, the best solution is to use std::getline . then use the line that was entered to build std::istringstream to parse the line. This leaves the input stream in good condition and is already synchronized for the next input. I would 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; } 
+1
source

Source: https://habr.com/ru/post/914489/


All Articles