Strange behaviour when reading in int from STDIN - c++

Suppose we have a menu which presents the user with some options:
Welcome:
1) Do something
2) Do something else
3) Do something cool
4) Quit
The user can press 1 - 4 and then the enter key. The program performs this operation and then presents the menu back to the user. An invalid option should just display the menu again.
I have the following main() method:
int main()
{
while (true)
switch (menu())
{
case 1:
doSomething();
break;
case 2:
doSomethingElse();
break;
case 3:
doSomethingCool();
break;
case 4:
return 0;
default:
continue;
}
}
and the follwing menu():
int menu()
{
cout << "Welcome:" << endl
<< "1: Do something" << endl
<< "2: Do something else" << endl
<< "3: Do something cool" << endl
<< "4: Quit" << endl;
int result = 0;
scanf("%d", &result);
return result;
}
Entering numerical types works great. Entering 1 - 4 causes the program to perform the desired action, and afterwards the menu is displayed again. Entering a number outside this range such as -1 or 12 will display the menu again as expected.
However, entering something like 'q' will simply cause the menu to display over and over again infinitely, without even stopping to get the user input.
I don't understand how this could possibly be happening. Clearly, menu() is being called as the menu is displayed over and over again, however scanf() is part of menu(), so I don't understand how the program gets into this error state where the user is not prompted for their input.
I originally had cin >> result which did exactly the same thing.
Edit: There appears to be a related question, however the original source code has disappeared from pastebin and one of the answers links to an article which apparently once explained why this is happening, but is now a dead link. Maybe someone can reply with why this is happening rather than linking? :)
Edit: Using this example, here is how I solved the problem:
int getNumericalInput()
{
string input = "";
int result;
while (true)
{
getline(cin, input);
stringstream sStr(input);
if (sStr >> result)
return result;
cout << "Invalid Input. Try again: ";
}
}
and I simply replaced
int result = 0;
scanf("%d", &result);
with
int result = getNumericalInput();

When you try to convert the non-numeric input to a number, it fails and (the important part) leaves that data in the input buffer, so the next time you try to read an int, it's still there waiting, and fails again -- and again, and again, forever.
There are two basic ways to avoid this. The one I prefer is to read a string of data, then convert from that to a number and take the appropriate action. Typically you'll use std::getline to read all the data up to the new-line, and then attempt to convert it. Since it will read whatever data is waiting, you'll never get junk "stuck" in the input.
The alternative is (especially if a conversion fails) to use std::ignore to read data from the input up to (typically) the next new-line.

1) Say this to yourself 1000 times, or until you fall asleep:
I will never ever ever use I/O functions without checking the return value.
2) Repeat the above 50 times.
3) Re-read your code: Are you checking the result of scanf? What happens when scanf cannot convert the input into the desired format? How would you go about learning such information if you didn't know it? (Four letters come to mind.)
I would also question why you'd use scanf rather than the more appropriate iostreams operation, but that would suffer from exactly the same problem.

You need to verify if the read succeeded. Hint: it did not. Always test after reading that you successfully read the input:
if (std::cin >> result) { ... }
if (scanf("%d", result) == 1) { ... }
In C++ the failed state is sticky and stays around until it gets clear()ed. As long as the stream is in failed state it won't do anything useful. In either case, you want to ignore() the bad character or fgetc() it. Note, that failure may be due to having reached the end of the stream in which case eof() is set or EOF is returned for iostream or stdio, respectively.

Related

What happens when c++ expects one data type and gets another?

I am new to c++ and was making a program in c++11 that sorts a list of integers using the bubble sort algorithm. While I was doing this I noticed something weird. This is my code:
#include <iostream>
void bubbleSort(int x) {
bool done;
int list[x] {0};
std::cout << "List:\n";
for (int i=0;i<x;i++) {
std::cout<<i<<':';
std::cin>>list[i];
}
do {
done = true;
for (int i=0;i<x-1;i++) {
if (list[i]>list[i+1]) {
list[i] = list[i]+list[i+1];
list[i+1] = list[i]-list[i+1];
list[i] = list[i]-list[i+1];
done = false;
}
}
} while (not done);
for (int i:list) {
std::cout<<i<<' ';
}
std::cout<<std::endl;
}
int main() {
int n;
std::cout<<"Length of list: ";
std::cin>>n;
bubbleSort(n);
}
If I input a char instead of an int the program outputs numbers leading up to the length of the list then a string of zeros equal to length of the list.
ex: if I input 5 then type 'k' at the input:
1:2:3:4:0 0 0 0 0
My question is, why is it producing this specific output? I would expect an error if it gets the wrong data type. Sorry if my question is confusing. Thanks in advance.
If you enter k when the input is expecting a number. Then the stream will go into an error state.
The problem is that you did not check the state:
std::cin>>n;
// There could be an error in the line above.
// But you did not check for the error.
Also here:
std::cin>>list[i];
// There could be an error in the line above.
// But you did not check for the error.
Try this:
if (std::cin >> n) {
std::cout << "It worked I got the number: " << n << "\n";
}
else
{
std::cout << "Failed to read a number.\n";
}
How does the above work.
Well the result of the operator>> is a reference to a stream. So it reads a value from the stream into n but returns a reference to the stream. This allows you to things like this:
std::cin >> n >> x >> y;
After each operator>> you get a reference to the stream to apply to the next operator>> so you can chain reads together.
When you use a stream in a boolean context (a test like an if or while) it will convert itself to boolean value depending on its internal state. If the internal state is good std::cin.good() then it will return true otherwise it returns false.
So after it completes the operator>> in then converts itself to bool for the if statement. If it is in a good state you know the read worked. If the read failed it would set an internal fail state and good() returns false.
So what happened in your code.
Well the read failed and the state of the stream was set to failed. When a read fails the preferred behavior is that object being read into remain unchanged (this is what happens for POD (standard) types, user defined types this can be a bit more haphazard).
So the value of n remains unchanged.
When you declared n
int n;
You did not define an initial value so it has an indeterminate value. Which means trying to read that value is UB. UB is bad. it means the code can do anything (which it has done). In practical terms (for most systems) it means the variable has an unknowable value and is whatever was left at that memory location from the last variable that used it.
For your specific case:
So you have typed 5 first then k.
So your first read std::cin >> n; worked.
The next read std::cin>>list[i]; failed.
This set the state of the stream to bad. Any subsequent reads do nothing (until you reset the stream state to good). So you are supposed to detect and fix the stream state.
Each subsequent time around the loop the std::cin >> list[i] will do nothing as the stream is in an error state. Which means it will keep its original value (which for this case is defined as zero 0).
Again the correct action here is to read and check the state of the stream. If it fails take corrective action:
if (std::cin >> list[i]) {
// Worked
}
else {
std::cerr << "Bad input. Try again\n";
// reset the state of the stream
// before trying to read again.
std::cin.clear();
if (std::cin >> list[i]) {
std::cerr << "You got it correct this time\n";
}
else {
std::cerr << "User not bright enough to use the app aborting\n";
throw std::runtime_error("Failed Bad User");
}
}
Additional Note
This behavior of streams is good for reading user input. As it allows a natural flow for detecting and writing code for the user to fix the issue. This design is practically the same for all modern languages that have the same pattern.
But this is not a good flow when you have machine input (ie. there are not expected to be any errors in the input and if there was an error there is no way to correct it).
For reading machine input you can set the stream to throw on an error. This allows you to write nice clean easy to read code that when things go wrong (when they should not) then an exception is throw causing the application to correctly terminate (or the exception could be caught).
std::cin.exceptions(std::ios::badbit); // Fail and Bad

Proper use of EOF (can it be used multiple times in a program?)

In my intro to programming course (c++), the instructor told us to:
"Write a program that inputs an unspecified number of integer ("int")
values and then outputs the minimum value and the maximum value that
were entered. At least one value will always be input. (read all input
until EOF). For extra credit make your program also work when the user
doesn't enter any ints at all (just the EOF.)"
I wanted to get fancy, so, in my solution, when just EOF is entered, the program responds with "Oops! You didn't enter anything. Please try again, this time entering at least one integer: " and prompts for input again.
My instructor is saying that this answer is wrong because
After the EOF, there should be no more input to a program (neither
expected by the user nor the program) — using the EOF to switch from
“one mode” of input to another mode of input isn’t supporting the
standards.
Every definition of EOF I've found on the internet doesn't seem to support my professor's definition. EOF, from what I can tell, is simply defined as the end of the current file. It seems perfectly valid to accept input from a user until EOF, do something with that input, and then ask for additional input until EOF again.
Because this is an online course, I was able to review everything we learned relating to EOF and we were only told that EOF meant "End of File" and could be 'used to signal an end to user input' (important, because, even if my professor was wrong, one could argue that I should have adopted his standards if he had specifically told us to. But he didn't tell us to).
What is the proper way to use EOF with user input? Is my professor's statement that "After the EOF, there should be no more input to a program" the standard
and expected way to use EOF? If a program accepts a variable amount of input, does something with it, and then accepts more variable input, is it not acceptable to use EOF with those inputs (aka don't use while(cin >> user_input) in that scenerio)? If so, is there a standard for what should be used to signal end of input in a scenario where you're accepting variable input multiple times?
My exact solution to the assignment is below. My solution to the main assignment "Write a program that inputs an unspecified number of integer ("int") values and then outputs the minimum value and the maximum value that were entered" was considered correct, by the second part of the assignment "make your program also work when the user doesn't enter any ints at all (just the EOF.)" was deemed incorrect ("make the program also work" is the only prompt we were given).
Thanks so much for any feedback!! Obviously, I'm skeptical of my professors feedback / decision, but, in general, I'm just trying to get a sense of C++ community standards.
#include <iostream>
#include <iomanip>
#include <string>
#include <stdlib.h>
using namespace std;
int main(){
string user_input;
int int_input, min_user_input, max_user_input;
bool do_it = true;
cout << "Hi John," << endl;
cout << "Please enter a few integers (signal EOF when finished): ";
while(do_it) {
cin.clear();
cin >> user_input;
if (user_input.empty()) {
cout << endl;
cout << "Oops! You didn't enter anything. Please try again, this time entering at least one integer: ";
}
else {
try {
int_input = atoi( user_input.c_str() );
min_user_input = int_input;
max_user_input = int_input;
while(cin >> int_input) {
if (min_user_input > int_input) {
min_user_input = int_input;
}
if (max_user_input < int_input) {
max_user_input = int_input;
}
}
cout << endl;
cout << "The max user input was: " << max_user_input << endl;
cout << "The min user input was: " << min_user_input << endl;
do_it = false;
}
catch (std::invalid_argument) {
cout << endl;
cout << "Oops! You didn't enter an integer. Please try again, this time only entering integers: ";
do_it = true;
}
}
}
return 0;
}
Note: additional feedback I got on this submission was: to not use c libraries (apparently stdlib.h is one) and that, on some computers (though, apparently, not mine), #include <stdexcept> will be needed to compile.
Answer
Short answer: my instructor is correct. When used with cin, no additional user input should follow an EOF signal. Apparently, in some cases the program won't let you enter more information, but, as #hvd points out
Although your system may let you continue reading from the same file
in the specific case that it is coming from a TTY, due to EOF being
faked there, you shouldn't generally rely on that.
Aka, because I'm using a terminal to enter user input, the program happens to work. In general, it won't work though.
As #RSahu answers, EOF just shouldn't be used to signal the end of variable length cin multiple times in a program. Importantly
There is no standard means, or commonly practiced coding standard, of
indicating when user input has ended for the time being. You'll have
to come up with your own mechanism. For example, if the user enters
"end", you can use it to deduce that the user has ended input for the
time being. However, you have to indicate to the user that that's what
they need to enter. Of course, you have to write code to deal with
such input.
Because this assignment required the use of EOF, what I was attempting to accomplish was, unintentionally, prohibited (aka receive input, check it, possibly receive more input).
Proper use of EOF (can it be used multiple times in a program?)
There is no single EOF. There is EOF associated with every input stream.
If you are reading from a file, you can reset the state of the std::ifstream when it reaches EOF to allow you to read the contents of the file again.
However, if you are reading data from std::cin, once EOF is reached, you can't read from std::cin any more.
In the context of your program, your professor is right. They are most likely talking about reading from std::cin.
EOF, from what I can tell, is simply defined as the end of the current file.
It is. Note that in particular, what it doesn't mean is the automatic start of a new file.
Although your system may let you continue reading from the same file in the specific case that it is coming from a TTY, due to EOF being faked there, you shouldn't generally rely on that. Try program </dev/null and see happens when you try to automate your program.

Holding the console-screen when end-of-file is involved

Here's the scaled down version of the program which accepts an unknown no. of Integer inputs. I used cin.get() before but to no avail, finally used this but unfortunately it too didn't worked. I am using Notepad++ spawning command prompt to run my programs. Is this something to do with Notepad++ OR the CTRL-Z (end-of-file) character?
EDIT : Works fine using cmd.exe
vector<int> vint;
int val = 0;
cout << "Enter integers..... Press CTRL and \'Z\' when done entering!"
<< "\n GO... : ";
while(cin >> val)
vint.push_back(val);
if (vint.size() > 1)
{
...
}
else
{
...
}
std::cin.ignore (std::numeric_limits<std::streamsize>::max(), '\n' );
std::cin.get();
When you enter Ctrl+Z in a console programme you tell that it's the end of the file. Any subsequent reading from cin is then doomed to fail.
It works from the command line, because the command processor doesn't close the window when the programme is over.
Possible solutions:
The portable approach would be to interupt the loop cleanly by checking for a special value (for example 0).
If this is not possible, another approach would be to gain more control on the user input and read lines into a string. You could then end the loop when an empty line is entered. This is I think for the user the most intuitive approach. All you have to do is to parse non empty strings with stringstreams (and eventually complain if non numeric values were entered).
An less perfect approach could be to instruct the user to enter some non numeric value to end the loop. You then have to clear the failure that invalid input would generate:
while (std::cin >> val ) {
...
}
if (std::cin.eof()) // display the special case
std::cout <<"End of file encountered !" << std::endl;
std::cout << "Press a key...";
std::cin.clear(); // clear the error state of cin
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cin.get();
Surprisingly, this works compiled with MSVC2015 on windows when entering Ctr+Z: once the end of file state cleared the console is magically restored and you can continue to read. However you can't assume this to work with console front-ends like Notepad++, nor with other implementations of the standard library, nor on other OS.

Program won't quit if user enters -1 while using return 0;

Here's one of the return 0; sections I have in my main() function:
cout << "Would you like to remove a Pokemon? (Y/N): ";
cin >> removePokemonChoice;
cin.ignore(5, '\n');
if (toupper(removePokemonChoice) == 'Y')
{
deletePokemon();
cout << "New list:" << endl;
displayPokemon();
}
if (removePokemonChoice == -1)
return 0;
currently, it won't exit out of the program if the user enters -1. I can't use an else statement because I want the program to loop (there's a similar question after this bit that I want to come after) until -1 is entered.
Your program asks "(Y/N)" so I suppose removePokemonChoice is a char type... it is strange to expect -1 value from char data. By the way "-1" need 2 characters, so it must be string :-)
Think of it this way:
You're writing a program that asks you whether or not you want to remove a Pokemon. Normally, like in the actual game itself when Prof. Oak prompts you with a confirmation of do you want Charmander (and the answer is yes, you always go with the fire type, no exceptions). The choices the game will always give you is "Yes" or "No".
Would you ask a yes/no question and expect a numerical answer back? While it is true the compiler converts and does numerical stuff in the background, what you really should be thinking about is, is it intuitive to the user?

Flush out all other characters in input buffer

I am trying to control the usage of a small console application, and don't want the user to type anything more than one character. What I have so far
int main()
{
char choice;
string strChoice;
/*Set the title bar title to Binary Calculator*/
system("title Binary Calculator 2014");
do{
Menu();
getline(cin,strChoice);
choice = toupper(strChoice[0]); //convert value to uppercase for conformity
DetermineChoice(choice);
} while (mistakes < 3);
}
But when I type
bbbbbbbb
The screen goes buggy as all hell ( I believe it's caused from the do while loop ) so I need to just flush all characters besides the first one. Also when I select B the first time the program runs, then I go back and try to select B again it says I don't have anything but a carriage return in the input buffer.
Above is my int main. I'll show you the determine choice function and the error handling function.
void DetermineChoice(char value)
{
/*
Purpose: Determine what character was passed to value
Pre: a hopefully valid character
Post: Will go through cases below and then pass to another function
*/
string binary;
int decimal;
switch (value)
{
case 'B':
case 'C':
ConversionOperation(value);
case 'P':
cout << "Process File" << endl;
break;
case '+':
case '-':
case '/':
case '*':
case '%': ArithmeticActions(value);
case 'Q':
PrintSummary();
break;
default:
HandleChoiceError(value);
break;
}
}
Choice Error:
void HandleChoiceError(char value)
{
/*
Purpose: Handles the errorenous character passed
Pre: an invalid character
Post: Will output the error, pausing the screen then reprint the menu
*/
system("cls");
mistakes++;
cout << setw(40) << "The option you selected (" << value << ") is not a valid choice." << endl;
cout << setw(25) << "You have made " << mistakes << " mistake" << (mistakes > 1 ? "s" : "") <<" becareful only three allowed!" << endl;
cout << setw(60) << "Please press enter and try again" << endl;
if (mistakes < 3)
{
system("pause");
}
}
Some things that need to be aware of::
I can only use system (so please don't tell me it's bad or resource hog!)
I can't use fflush or any flushing besides cin.clear()
I can't use any other libraries besides iostream , iomanip , fstream , string , and ctype.h
Thanks everyone I now have the program working correctly.
int main()
{
char choice;
string strChoice;
/*Set the title bar title to Binary Calculator*/
system("title Binary Calculator 2014");
do{
Menu();
if ( getline(cin, strChoice) )
{
choice = toupper(strChoice[0]);
DetermineChoice(choice);
cin.ignore(10000, '\n');
}
else
{
cout << "Something went wrong with the input, please restart the program and try again." << endl;
break;
}
} while (mistakes < 3);
return 0;
}
There is no such thing as "flushing" an std::istream. Flushing is an output concept. Since there are multiple buffers for input from the console (the input buffer inside the std::istream's std::streambuf and an operating system consolde buffer) there is no reliable way to actually get rid of all input characters. You can get rid of all characters by disabling the concole's buffer (on UNIXes you'd use tcsetattr() and tcgetattr() to clear the ICANON flag).
The approaches which should be good enough for you needs are to either ignore all charactesr on the current line or to remove all characters in the input buffers:
To remove all characters on the currently you'd use std::istream::ignore() with the maximum number of characters to be ignored and the character where to stop, i.e., '\n' for the newline. To match as many characters as needed, you'd pass the magic value std::numeric_limits<std::streamsize>::max() (a reasonably large value, say 10000, would do the trick for practical needs, too).
You can find out a lower bound of characters immediately available using in.rdbuf()->in_avail(). This function determines how many characters can be read without the stream blocking. In practice, this is the number of characters in the std::istream's input buffer, i.e., something like in.ignore(in.rdbuf()->in_avail()) should remove all characters.
Pesronally, I would go with using in.ignore(count, '\n') with a suitable count (I'd obviously use std::numeric_limits<std::streamsize>::max() but it seems you can't use this function, probably because I'm currently helping with your homework assignment). Of course, std:getline() already reads the entire line anyway, i.e., there isn't really anything to be ignored.
Note that you should always verify whether the input operation was actuall successful:
if (std::getline(std::cin, line)) {
// process successful line
}
else {
// deal with the input having failed
}
Note, that input of a line is successful even when the line is empty! Before accessing the first character you should verify that it is present, e.g., using !line.empty().
BTW, as you have mentioned clear(): that actually doesn't ignore any characters. Instead, it clears the stream error flags, i.e., what is being tested to verify if input is successful. While being on notes: the argument to any of the <cctype> functions has to be a non-negative int in the value range of unsigned char plus EOF. Since char tends to be signed, passing an arbitrary char, e.g., strChoice[0] can result in undefined beheavior (e.g. when passing an ISO-Latin-1 or UTF-8 representation of my second name). The normal fix is to use
std::toupper(static_cast<unsigned char>(strChoice[0]))
Easiest to patch in: Replace
cin.ignore(1);
with
cin.ignore(numeric_limits<streamsize>::max(), '\n');
and
#include <limits>
at the top of the file. What this means is: "ignore all characters in the stream up to the next newline character (\n)". This is because
cin.ignore(n, c);
means "ignore n characters from cin, but stop after you found c," and numeric_limits<streamsize>::max() is the largest value in the streamsize type and a special case for istream::ignore where it means infinity.
This will work nicely for your use case because user input is generally line-based. It is better than discarding the input buffer because what's in the input buffer is not always predictable (someone could pipe a file to your program, the terminal could buffer strangely, the user could be a very fast typist, among other things). Discarding the input buffer wholesale will sometimes yield strange results, but nobody will be terribly surprised if you discard the rest of the line after a command.