I've been learning C++, and this chunk of code is from a simple grading program. But when I try to get the user input, there's a problem.
If I enter a number, whether it be less than 0 or more than 100, or anything in between, my loop works fine.
But if I type in any letter, or any non-alphanumeric character (ex: +, (, %, etc.) I get an infinite loop with "Please enter a grade value between 0 and 100" printed forever.
What am I doing wrong?
Thanks.
int _tmain(int argc, _TCHAR* argv[])
{
using namespace std;
int grade = -1; // grade will hold grade value; initialized to -1
do {
cout << "Please enter a grade value between 0 and 100." << "\n";
cin >> grade;
} while (grade < 0 || grade > 100);
cout << grade << "\n";
printGrade(grade);
return 0;
}
if cin>>grade fails (aka can't parse as int) it does not consume the stream. You can try:
int main()
{ using namespace std;
int grade = -1; // grade will hold grade value; initialized to -1
do {
cout << "Please enter a grade value between 0 and 100." << "\n";
if (!(cin >> grade))
{
cin.clear();
}
} while (grade < 0 || grade > 100);
cout << grade << "\n";
return 0;
}
But this is only part of the problem. Really, you should use std::getline and parse the grade as a full line for correct input.
If cin does not receive valid input for the data type (int), the variable grade is not changed and remains at -1. You can test whether the input was successful like so
bool success = (cin >> grade);
if (! success)
{
cin.clear();
cout << "bad input\n";
break;
}
You can also use this as a shortcut if (! (cin >> grade))
Note that you need to clear the error state of cin before you use it again.
I'm pretty sure the cin failed so you may need to reset its fail flag or something like that.
Add this to your loop:
if (cin.fail())
{
cout << "failed";
cin.clear();
}
Correctly and safely reading until you get valid input is far trickier than you'd think. If there's invalid input, like a letter, the stream is set in a "failure" state, and refuses to read any more characters until you clear the state. But even if you clear the state, that input is still waiting there, in the way. So you have to ignore those characters. The easiest thing to do is simply ignore everything until the next enter key, and then try the input again.
But it gets even more complicated, because if the stream has an error, it gets set in a "bad" state, or if it reaches the end of the stream, it gets set in a "eof" state. Neither of these two are recoverable, so you must detect them and quit the program to avoid an infinite loop. Even more irritating, istreams have a .fail() function, but that checks if it's in fail or bad, which makes it nearly useless in my opinion. So I wrote a little invalid_input which checks if the stream can continue.
Note that get_grade sets the fail flag manually if the input is out-of-range.
#include <iostream>
#include <stdlib.h>
#include <limits>
bool invalid_input(std::istream& in)
{return in.rdstate() == std::ios::failbit;}
std::istream& get_single_grade(std::istream& in, int& grade) {
std::cout << "Please enter a grade value between 0 and 100." << "\n";
if (in>>grade && (grade<0 || grade>100))
in.setstate(std::ios::failbit);
return in;
}
bool get_grade(std::istream& in, int &grade) {
while(invalid_input(get_single_grade(in, grade))) { //while we failed to get data
in.clear(); //clear the failure flag
//ignore the line that the user entered, try to read the next line instead
in.ignore(std::numeric_limits<std::streamsize>::max(),'\n');
}
return in.good();
}
int main(int argc, char* argv[]) {
int grade = -1; // grade will hold grade value; initialized to -1
if (get_grade(std::cin, grade) == false) {
std::cerr << "unxpected EOF or stream error!\n";
return false;
}
std::cout << grade << "\n";
return EXIT_SUCCESS;
}
As you can see here, this doesn't get into an infinite loop when given out of bounds numbers, end of file, a stream failure, or invalid characters.
Related
I'm trying to only allow integer values into my program, so I've made the following function. The function is similar to other ones I've seen online, and mine seems to work just fine up until I add an ! in front of it to check if something is not an int.
Function to check if input is an integer:
bool isInteger(std::string s)
{
for (int i = 0; i < s.length(); i++)
{
if (isdigit(s[i]) == false)
{
return false;
}
return true;
}
}
Function being put to use:
int getLevel()
{
int level;
std::cout << "Level One\n";
std::cout << "Level Two\n";
std::cout << "Level Three\n";
std::cout << "Level Four\n";
std::cout << "Level Five\n";
std::cout << "Enter your level (1-5): ";
std::cin >> level;
while (!isInteger(std::to_string(level)) || level < 1 || level > 5)
{
std::cout << "Enter an integer value between 1-5 inclusive: ";
std::cin >> level;
}
clrscr();
return level;;
}
I believe the function works just fine until I put the ! in front of it. I am trying to only allow integer input into my program, and when I enter a double or string, the console becomes flooded with the message "Enter an integer value between 1-5 inclusive: " and doesn't give any time to enter an input. I am fairly new to c++ programming and could use some advice. Thank you!
std::cin >> level;
will try to read an integer and it will never read anything other than an integer. If this fails std::cin's failbit is set and further input operations (like std::cin >> level; inside the loop) are skipped.
You need to check if the reading succeeded and ignore the current input if not. Like this for example:
std::cout << "Enter your level (1-5): ";
while(!(std::cin >> level) || level < 1 || level > 5) {
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cout << "Enter an integer value between 1-5 inclusive: ";
}
As little semi-related hint: level will always be an integer. Converting it to a string will always be the string-representation of an integer, so isInteger(std::to_string(level)) will always be true, unless level is negative, because you don't check for the sign.
Also that return true; in isInteger must be outside the loop, else you only check the first character.
Thanks to all the replies and clarification, I've managed to come up with a solution of my own.
New isInteger function that now checks for everything that is needed including inputs like "0004" that a user suggested above:
bool errorCheck(std::string s)
{
int intLevel;
std::stringstream tempLvl(s);
tempLvl >> intLevel;
for (int i = 0; i < s.length(); i++)
{
if (isdigit(s[i]) == false || s[0] == '0' || intLevel < 1 || intLevel > 5)
{
return false;
}
}
return true;
}
The method in action:
std::cout << "Enter your level (1-5): ";
std::cin >> stringLevel;
while (!errorCheck(stringLevel))
{
std::cout << "Enter an integer value between 1-5 inclusive: ";
std::cin >> stringLevel;
}
std::stringstream lvl(stringLevel);
lvl >> level;
clrscr();
return level;
}
Please let me know if you spot any problems with the code or have any easier solutions. Thanks for all the help!
ok i am gonna tell u the fact that console input extracts the input from console so if u ever tried to do something like that
i.e read string in place of integer the cin is going to be in bad state you can check this fact by putting an if like this
if(!cin>>level) break;
and u will find it working actually stream takes input from the console and convert it to boolean value so u can always check it's state bad state return false else true...... ..
SO,finally the bug is in cin>>level...
I hope u understood.... also check out that return true statement..
i am gonna put u reference link for more answer on this bug...
user enters String instead of Int
I am currently working on a text based adventure game as a project for class. I have mostly everything started and working fine. The only problem is when I ask the user which room they want to change to, if they enter a blank input, then a message should output saying "You must choose a room." For the life of me I cannot figure it out. Any help is much appreciated.
Code:
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
int main() {
bool game_play = true;
bool game_start = true;
int room_change;
int room_current = 0;
while (game_play == true) {
if (game_start == true) {
srand((unsigned int)time(NULL));
room_change = rand() % 2 + 1;
game_start = false;
}
else {
for (bool check = false; check == false;) { // Check if input is invalid
cin >> room_change;
if (cin.fail()) {
cout << "Choose an existing room.";
cin.clear();
cin.ignore();
}
else if (room_change == room_current) {
cout << "You're already in that room.";
}
else {
check = true;
}
}
}
switch (room_change) {
case 1:
cout << "You are in room 1.";
room_current = 1;
break;
case 2:
cout << "You are in room 2.";
room_current = 2;
break;
case 3:
game_play = false;
break;
default:
cout << "That room doesn't exist.";
}
}
return 0;
}
I just ran your code and when you hit enter, it will keep waiting until you enter a number or something invalid such as a character or a string. I did find that if you change your code from
cin >> room_change;
to
cin >> noskipws >> room_change;
when the user inputs a blank, it will cause the cin.fail() to return true and then proceed to print "Choose an existing room."
In your situation, the while loop will keep getting called until we have valid input. The "Choose an existing room" does get repeated because room_change is an integer, so when we hit enter, the '\n' will be left in the buffer. The while loop on the next iteration then reads that '\n' and executes the cin.fail() before letting you input something else. One solution I found is to use more cin.ignore() statements.
for (bool check = false; check == false;) { // Check if input is invalid
cin >> noskipws >> room_change;
if (cin.fail()) {
cout << "Choose an existing room.";
cin.clear();
cin.ignore();
} else if (room_change == room_current) {
cout << "You're already in that room.";
cin.ignore();
} else {
check = true;
cin.ignore();
}
}
The reason is because we want to get rid of that '\n' so that the cin.fail() does not execute. However, I did find that when you input a character, it will print "Choose an existing room" twice. It will print the first time because a character is not an integer, and a second time because of that '\n'.
The only problem is when I ask the user which room they want to change to, if they enter a blank input, then a message should output saying "You must choose a room."
Using std::getline and then extracting the number from the line using a std::istringstream is a better strategy for that.
std::string line;
std::cout << "Choose an existing room. ";
while ( std::getline(std::cin, line) )
{
// Try to get the room_change using istringstream.
std::istringstream str(line);
if ( str >> room_change )
{
// Successfully read the room.
break;
}
// Problem reading room_change.
// Try again.
std::cout << "Choose an existing room. ";
}
#include <iostream>
using namespace std;
int main(){
int room_change=200;
cout<<"Enter Blank";
cin>>room_change;
if(room_change==NULL){
cout<<"There is NO-THING"<<endl;
}
if(room_change!=NULL){
cout<<"There is something and that is :"<<room_change<<endl;
}
return 0;
}
But a much simpler approach to this would be to use Strings. If this is a Homework of sort and you are limited to Integer variable only. Its much more complicated if you want to detect if an Buffer is empty or not. Regardless of homework limitation, the OS layer input is String based. How can I use cin.get() to detect an empty user input?
When I run my code, it only prints the decimal parts of the double. On another page, I took a inputted double and printed out the double the way it was inputted.
But for my following code, it only prints out the decimals. For example, when I input 1.95 it only prints out 0.95. Why is it removing the first digit? I see nothing in my code that points to this.
I have already tried it in a more simple way and it worked. And I dont see any problems that would mess with the double in my code.
#include <iostream>
using namespace std;
int main()
{
double price;
char user_input;
do
{
cout << "Enter the purchase price (xx.xx) or `q' to quit: ";
cin >> user_input;
if (user_input == 'q')
{
return 0;
}
else
{
cin >> price;
int multiple = price * 100;
if (multiple % 5 == 0)
{
break;
}
else
{
cout << "Illegal price: Must be a non-negative multiple of 5 cents.\n" << endl;
}
}
} while (user_input != 'q');
cout << price << endl;
}
When I input 1.95, I get 0.95. But the output should be 1.95.
Problem covered in other answer: Reading for the 'q' removed the first character from the stream before it could be parsed into a double.
A solution: Read the double first. If the read fails, check to see if the input is a 'q'.
#include <iostream>
#include <limits>
using namespace std;
int main()
{
double price;
while (true)
{
cout << "Enter the purchase price (xx.xx) or `q' to quit: ";
if (cin >> price)
{
// use price
}
else // reading price failed. Find out why.
{
if (!cin.eof()) // didn't hit the end of the stream
{
// clear fail flag
cin.clear();
char user_input;
if (cin >> user_input && user_input == 'q') // test for q
{
break; // note: Not return. Cannot print price if the
// program returns
}
// Not a q or not readable. clean up whatever crap is still
// in the stream
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
}
else
{
// someone closed the stream. Not much you can do here but exit
cerr << "Stream closed or broken. Cannot continue.";
return -1;
}
}
}
cout << price << endl;// Undefined behaviour if price was never set.
}
Another reasonable alternative is to read all input as std::string. If the string is not "q", attempt to convert it to a double with std::stod or an std::istringstream.
When you type 1.95 in the command line, variable user_input gets assigned '1', and price gets assigned .95.
I'm trying to set a basic program that asks for Age, And if the Number entered by the user is less than 99, It will say "Perfect". If the number is MORE than 99, it will say "You Can't be that old, Try again". Additionally, if the user enters something that is not a number (like a letter "m, r" or anything else like "icehfjc") then it will say "That is not a number."
This is my code so far:
#include <iostream>
#include <string>
using namespace std;
int main()
{
int age;
backtoage:
cout << "How old are you?\n";
cin >> age;
if (age < 99)
{
cout << "Perfect!\n";
system("pause");
}
if (age > 99)
{
cout << "You can't be that old, Try again.\n";
system("pause");
system("cls");
goto backtoage;
}
Else
{
cout << "That is not a number, Please Enter a Valid Number\n";
system("pause");
system("cls");
goto backtoage;
}
}
I know "Else" doesn't work because C++ treats letters as integers as well, so
if I write "m" it will take it as a >99 number (because of the integer value of "m") therefore displaying the "you can't be that old" message. but how can I fix this so the program displays "Please enter a number" when a letter is entered? (If anyone could fix the code and write it in a way that works, I'd
be forever grateful).
Any suggestions, tips or hints are very welcome.
so if I write "m" it will take it as a >99 number (because of the integer value of "m")
No, "m" can't be inputted into an int, cin will fail here. So what you should do is to check the status of cin, such as
if (cin >> age) {
// ok
if (age < 99)
{
...
} else
{
...
}
}
else
{
// failed
cout << "That is not a number, Please Enter a Valid Number\n";
system("pause");
system("cls");
cin.clear(); // unset failbit
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // skip bad input
goto backtoage;
}
Check the behavior of std::basic_istream::operator>>
If extraction fails (e.g. if a letter was entered where a digit is expected), value is left unmodified and failbit is set.
BTW: goto is almost obsolete in modern c++ programming. It should be easy to implement the same logic with a loop.
LIVE
You can Try it.It will validate numeric input in C++.Here cin.good() function is return true if the input is valid, if it is not valid it will return fase. cin.ignore() is used to ignore the rest of the
input buffer, which contains the erroneous input and cin.clear() is
used to clear the flag.
#include <iostream>
#include<string>
#include <limits>
using namespace std;
int main() {
backtoage:
int age = 0;
cout << "How old are you?\n";
cin >> age;
if(cin.good()){
if (age < 99){
cout << "Perfect!\n";
system("pause");
}
else if (age > 99){
cout << "You can't be that old, Try again.\n";
system("pause");
system("cls");
goto backtoage;
}
}
else{
cout << "That is not a number, Please Enter a Valid Number\n";
system("pause");
system("cls");
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
goto backtoage;
}
return 0;
}
Input/Output:
How old are you?
k
That is not a number, Please Enter a Valid Number
How old are you?
120
You can't be that old, Try again.
How old are you?
10
Perfect!
first of all , if the users can enter characters like 'm' 'k' or anything else , I suggest using string , then if you want to change to number just substract '0' , so you can work with numbers, secondly programmers , usually do not use goto statement because it can be dangerous and make underfiend behaivor appear into your program.
Sorry for my bad english.
#include<iostream>
#include <string>
using namespace std;
int main()
{
string input;
getline(cin, input);
unsigned int lenght = input.size(),age=0;
for (int i = 0; i < lenght; ++i) {
if (input[i] >= '0' && input[i] <= '9') {
age = age*10+(int)input[i]-'0';
}
}
if (age > 99)
cout << "Nice try , you can`t be that old\n";
else
cout << "perfect!\n";
return 0;
}
I just started learning C++ after previously coding with Java. The code below takes input from the user and validates the input. The first piece asks for the number of voters, which must be a positive number. If I enter a negative number the program behaves as I expected. It prints out the error message and asks for the input again. However, if I enter any other character, such as any alphabet letter I get an infinite loop in the console, asking for input and printing the error message. What am I doing wrong?
my code:
#include <iostream>
using namespace std;
struct dataT {
int numOfVoters = -1;
float preSpread = -1;
float votingError = -1;
};
void getUserInfo() {
dataT data;
while (data.numOfVoters == -1) {
cout << "enter the number of voters" << endl;
cin >> data.numOfVoters;
if (data.numOfVoters <= 0) {
data.numOfVoters = -1;
cout << "Invalid entry, please enter a number larger than zero." << endl;
}
}
while (data.votingError == -1) {
cout << "enter the percentage spread between candidates" << endl;
cin >> data.votingError;
if (data.votingError <= 0 || data.votingError >= 1) {
data.votingError = -1;
cout << "Invalid entry. Enter a number between 0 to 1." << endl;
}
}
while (data.preSpread == -1) {
cout << "Enter the precentage spread between the two candidates." << endl;
cin >> data.preSpread;
if (data.preSpread <= 0 || data.preSpread >= 1) {
data.preSpread = -1;
cout << "Invalid input. Enter a number between 0 and 1." << endl;
}
}
}
int main() {
getUserInfo();
return 0;
}
Console:
enter the number of voters
f
Invalid entry, please enter a number larger than zero.
enter the number of voters
Invalid entry, please enter a number larger than zero.
enter the number of voters
Invalid entry, please enter a number larger than zero.
...
...
...
If you write cin >> integervariable but in cin there are character that cannot represent an integer, the input fails, !cin becomes true, and the character remain there until you don't reset the input state from the error and consume the wrong characters.
a proper check can be
while(integervariable has not good value)
{
cout << "prompt the user: ";
cin >> integervariable;
if(!cin) //something wrong in the input format
{
cin.clear(); //clear the error flag
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); //discard the rubbish
cout << "prompt error message \n";
}
}
Your if statements are always true, you want something more like:
if (data.votingError < 0 || data.votingError > 1) {
...
then data.votingError can take on a value different from -1 and exit your loop.
The std::cin object will check whether or not it is in a valid state every time it reads. If you enter a char where your program expects an int, then you'll "break" the input stream. All subsequent calls to std::cin will then be effectively skipped until you manually reset the input stream. When this happens, you'll never be able to set your values to anything other than -1, and your if statement always evaluates to true, causing an infinite loop.
As an aside, you can check for failure state by including && cin in all of your tests. Input objects implicitly evaluate to true if the stream is in a valid state and to false if the stream is in a failure state instead.