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.
Related
I am trying to implement a pause function in C++, but it is looping forever.
I am using macOS but I am trying to create a pause function that will work in any system... I believe my cin >> is not capturing '\n' or '\r' from the keyboard and it is looping forever.
void Transferencia::pause() {
char enter = 0;
while(enter != '\n' && enter != '\r') {
cout << "(Press Enter to Continue...) ";
cin >> enter;
}
cin.clear();
}
I want to pause my program until user press the key "enter".
But even when I press "enter/return" it keeps looping...
At very first: enter != '\n' || enter != '\r' is a tautology: Even if enter does equal one of the characters it cannot be equal to the other one. So one of the tests must be true... You actually want to stay in the loop when enter is unequal to both values.
std::cin >> ... won't read data before you press enter, but it will discard the newlines (actually, all whitespace). So it would suffice just to read one single character right without loop (the loop again would get an endless one); solely: If the user doesn't enter anything at all before pressing 'enter' key, there's no character to read from std::cin and we'd still be waiting.
What you can do is reading entire lines:
std::string s;
std::getline(std::cin, s);
That will accept empty lines as well, so does exactly what you want (note: no loop around!).
Edit (stolen from the comments; thanks, Thomas Matthews): An even more elegant way is
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
as it won't create any additional resources that would be discarded afterwards anyway (the std::string object!).
Edit 2:
Depending on type of last input operation, there might still be a newline (or even further data) buffered, e. g. after int n; std::cin >> n;. In this case, you need to skip the input yet buffered. So you would need ignore twice.
However, if the last input operation consumed the newline already (e. g. std::getline – or if there wasn't any preceding input operation at all), then this would lead to user having to press enter twice. So you need to detect what's has been going on before.
std::cin.rdbuf().in_avail() allows you to detect how many characters are yet buffered. So you can have:
if(std::cin.rdbuf().in_avail())
{
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
std::cout << "press enter" << std::endl;
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
On some systems (including mine), though, in_avail can return 0 even though a newline is yet buffered! std::cin.sync_with_stdio(false); can fix the issue; you should execute it before very first input operation. Hopefully, you don't use C++ (streams) and C (scanf, printf, etc) IO intermixed then...
The easiest way to do this is with getline().
cin >> ignores whitespace, newline characters included. getline() will read an entire line, newline character included. However, it does not copy the newline character to the output string. If the user simply hit the enter key and nothing else, you'd end up with an empty string.
So, to get your desired behavior, you would construct your loop like this:
string line;
while(true)
{
cout << "(Press Enter to Continue...) " << endl;
getline(cin, line);
if(line == "")
break;
}
#Aconcagua has answered your question but this is what I want to add in.
Normally, for handling some specific kind of event in computer, we usually follow event-driven paradigm or event-callback.
The idea is there is an event loop that waits for a new event coming into the system. This case, keyboard is an event, the event loop then calls event-callback. What event-callback does is it compares the value of input with some conditions then do some other tasks (it might change some state of the program or notify users).
The idea is keep CPU busy by either 2 ways.
event-driven : do other tasks while waiting for a new event
multithreading: multiple threads in the system. This approach has the disadvantage is at data-race
Have fun
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.
cout << "Would you like to make another transaction? (y/n)" << endl;
cin >> repeat_transaction;
static_cast<char>(repeat_transaction);
while (repeat_transaction != 'y' && repeat_transaction != 'n')
{
cout << "Invalid selection: Please enter y or n";
cin >> repeat_transaction;
static_cast<char>(repeat_transaction);
}
During the Invalid selection loop, I once accidentally pressed "mn". I noticed the console read out Invalid selection..., So, it did in fact finish and re-enter the while loop. However, after this the console terminated the program. If you enter a single character 'a' or 'y' or 'n' it acts just as it should. Ending or not ending. This was before I attempted to use static_cast to force the truncation of the user input.
Since you managed to get this program to compile I can only assume that repeat_transaction was specified as a char and not a std::string.
When you use cin to get a character it only gets one character but it doesn't flush the buffer. I believe you understand this issue since you wrote This was before I attempted to use static_cast to force the truncation of the user input. . You can attempt to use cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); instead of static_cast<char>(repeat_transaction); after each call to cin >> repeat_transaction; . There are downsides to this. If you enter 'mn' it will work as expected. It reads the m which is not y or n and then flushes the extra characters until it finds end of line \n. If you do nm, n will match and the m will be thrown away. So in that case it will accept nm as valid and exit the loop.
There are other ways that may be easier and give you the effect closer to what you are looking for. Instead of reading a character at a time you can read an entire line into a string using getline (See the C++ documentation for more information). You can then check if the length of the string is not equal to 1 character. If it's not length 1 then it is invalid input. If it is 1 then you want to check for y and n. Although basic (and not overly complex) this code would do a reasonable job:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string repeat_transaction;
cout << "Would you like to make another transaction? (y/n)" << endl;
getline(cin, repeat_transaction);
while (repeat_transaction.length() != 1 || (repeat_transaction != "y" && repeat_transaction != "n"))
{
cout << "Invalid selection: Please enter y or n";
getline(cin, repeat_transaction);
}
return 0;
}
I said reasonable job since one deficiency you might see is that you want to trim white spaces from the beginning and end. If someone enters n or y with a space or tab in front it will be seen as invalid (whitespace at the end would be similar). This may not be an issue for you, but I thought I would mention it.
On a final note, you may have noticed I used using namespace std;. I did so to match what was in the original question. However, this is normally considered bad practice and should be avoided. These StackOverflow answers try to explain the issues. It is better to not do it and prepend all standard library references with std::. For example string would be std::string, cin would be std::cin etc.
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.
I am new to C++ and am in a class. I am trying to finish the first project and so far I have everything working correctly, however, I need the user to input a number to select their level, and would like to validate that it is a number, and that the number isn't too large.
while(levelChoose > 10 || isalpha(levelChoose))
{
cout << "That is not a valid level" << endl;
cout << "Choose another level:";
cin >> levelChoose;
}
That is the loop I made, and it sometimes works. If I type in 11 it prints the error, and lets me choose another level. However if the number is large, or is any alpha character it floods the screen with the couts, and the loop won't end, and I have to force exit. Why does it sometimes stop at the cin and wait for user input, and sometimes not? Thanks for the help!
This is an annoying problem with cin (and istreams in general). cin is type safe so if you give it the wrong type it will fail. As you said a really large number or non-number input it gets stuck in an infinite loop. This is because those are incompatible with whatever type levelChoose may be. cin fails but the buffer is still filled with what you typed so cin keeps trying to read it. You end up in an infinite loop.
To fix this, you need to clear the fail bit and ignore all the characters in the buffer. The code below should do this (although I haven't tested it):
while(levelChoose > 10 || isalpha(levelChoose))
{
cout << "That is not a valid level" << endl;
cout << "Choose another level:";
if(!(cin >> levelChoose))
{
cin.clear();
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
}
Edit: numeric_limits<> is located in the limits include:
#include<limits>
From your description, it seems likely (nearly certain) that levelChose is some sort of numeric type, probably an integer.
When you use operator>> to read a number, anything that couldn't be part of a number (e.g., most letters) will be left in the input buffer. What's happening is that you're trying to read the number, it's failing and leaving the non-digit in the buffer, printing out an error message, then trying to read exactly the same non-digit from the buffer again.
Generally, when an input like this fails, you want to do something like ignoring everything in the input buffer up to the next new-line.
levelChoose appears to be an integer type of some form (int, long, whatever).
It's not valid to input a character into an integer directly like that. The input fails, but leaves the character in the incoming buffer, so it's still there when the loop comes around again.
Here's a related question: Good input validation loop using cin - C++
I suspect the part while(levelChoose > 10..... This does not restrict level to less than 10 (assuming greater than 10 is a large number in your context). Instead it probably should be while(levelChoose < 10...
To check that an expression is not too large, the following could be a possibility to validate (brain compiled code!!)
const unsigned int MAX = 1000;
unsigned int x;
cin >> x;
while(x < MAX){}