Code Snippet Works in Certain Cases but not as Expected, Why? - c++

I have this code snippet that is supposed to test whether the user enters an integer or not. This works if the user enters letters, but not decimals and I'm left wondering why that is. Here's my code snippet:
Student student;
int id;
while(!(cin >> id))
{
cout << "\nERROR: Please enter a Positive Whole Number" << endl;
cin.clear();
cin.ignore ();
cout << "Enter Student ID: ";
}
Entering A will make it iterate through the while loop, but if I enter 12.5 it drops out of the while loop and keeps going. Isn't it testing whether it will parse to integer or not? Why is it accepting 12.5 but not characters?

cin>>id will succeed as long as it finds something it can convert to an int ("12", in this case). When it reaches something it can't convert, it stops, but if it's read an int already, that counts as success.
To check that everything it read was digits, you might want to do something like using std::getline to read a line of input into a string, then use std::isdigit to test whether those are all digits. Testing a conversion to int (by itself) will only tell you that it found something that could be read as an integer, but won't tell you if that was followed by other things that couldn't be converted to an int.

Related

Can someone explain to me how this code works?(Program to ask for user input again if a numeric value expected and the user enters some other input)

I was reading a book on c++ (c++ primer plus) and found this code.
The purpose of the program is that if a numeric input is expected from the user to read it to ,say an array->
1)Reset cin to new input
2)Get rid of the bad input
3)Prompt the user to try again (exact words of the book)
Here is the code->(exact code copied from the book)
#include <iostream>
const int Max=5;
int main()
{
using namespace std;
//get data
int golf[Max];
cout<<"please enter your golf scores.\n";
cout<<"you must enter "<<Max<<" rounds.\n";
int i;
for(i=0;i<Max;i++) {
cout<<"round #"<<i+1<<": ";
while(!(cin>>golf[i])) {
cin.clear();
while(cin.get()!='\n')
continue;
cout<<"PLease enter a number: ";
}
}
double total=0.0;
for(i=0;i<Max;i++)
total+=golf[i];
cout<<total/Max<<" = average score "<<Max<<" rounds\n";
return 0;
}
The particular part which i don't understand is:
cin.clear();
while(cin.get()!='\n')
continue;
I'm a little unclear about the function of cin.clear() , the need of continue here , and what the while test conidtion does and how does it work.
A test run->
(italic part is the user input)
Please enter your golf scores.
You must enter 5 rounds.
round #1: 88
round #2: 87
round #3: duh
Please enter a number: 103
round #4: 94
round #5: 86
91.6 = average score 5 rounds
while(!(cin>>golf[i]))
{
cin.clear();
while(cin.get()!='\n')
continue;
cout<<"PLease enter a number: ";
}
First let's example the while condition. It seems to check if the value of the expression (cin >> golf[i]) is false. The falue of cin >> golf[i] is cin itself. So how can it be false? If a >> operation fails for the input stream, a flag named fail is set. That's what operator ! checks. So the condition means while after trying to read a number the object cin has fail set, do...
Now, once the fail flag is set, there isn't much you can do with an input stream. You won't be able to continue reading data from the stream unless you reset the fail flag. That's what cin.clear() does.
Then it munches the rest of the bad input line (which was NOT consumed since >> failed) by reading character by character until reaching the end of line.
And finally it prompts the user for new input.
Since golf[i] is an int, cin>>golf[i] will return true (or a value interpreted as true in earlier versions of C++ Standard Library) when the input is an int. Hence, while(!(cin>>golf[i])) will repeat until true is returned.
Inside the while loop you find code that ignores all input until a \n with the loop that stops only after receiving '\n' input:
while(cin.get()!='\n')
continue;
It also clears the flag indicating that cin is in a failure state by calling clear().
Note: This code makes an assumption that cin never ends, which makes it unsafe. If the input is taken from a file with invalid data, this program will be stuck in an infinite loop.

Visual C++ using Console: Char/String compatibility issues with while loop

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.

stop inupt if input no double

first of all I'm sorry for my
bad english.
I'm trying to read some numbers and write them into a vector in C++.
This should go as long as the input is a double number and the
loop should be stopped if the user writes an 'a'.
My Question is how can I check if the input is 'a'.
Breaking the loop is not the problem
while(true){
if(!(cin>>userInput)){
//here i want to know if the input is 'a' or some other stuff//
//also i want to do some other stuff like printing everything//
//what already is in the vector//
//when everything is done; break//
}
else
//the input is a valid number and i push it into my vector//
'userInput' is defined as double so the loop will stop.
My Problem is, if the user write 'q' the loop stops but it's instantly stoping the whole program. My try look like this:
while(true){ //read as long as you can
cout<<"Input a number. With 'q' you can stop: "<<endl;
if(!(cin>>userInput)){ //here the progam stops when the input is anything but a number
cout<<"How many numbers do you want to add up?"<<endl; //there are numbers in a vector that should be added up
cin>>numberOfAdditions;
break;
}
So I have a vector with some numbers the users writes down (20,50,90,...)
When the input is equal to 'q' (in this example everything but numbers )
the loop stops and I want to ask the user how many numbers should be added.
The cout-command is displayed but the input is beeing skipped.
So my program is not reading how many valued from the vector I want to add.
I hope you know what I mean and I don't want to use two questions and two variables to save the input but if it's not working without it I'll change my program.
Have a nice Day :)
Because your Input variable is of type double you have to flush the Input from cin before reading again. Otherwise there is still a newline in the buffer.
Consider the following example:
#include <iostream>
using namespace std;
int main(){
double userInput;
int numberOfAdditions;
while(true){ //read as long as you can
cout<<"Input a number. With 'q' you can stop: "<<endl;
if(!(cin>>userInput)){ //here the progam stops when the input is anything but a number
cout<<"How many numbers do you want to add up?"<<endl; //there are numbers in a vector that should be added up
cin.ignore (std::numeric_limits<std::streamsize>::max(), '\n');
cin.clear();
cin.ignore(INT_MAX,'\n');
cin >> numberOfAdditions;
break;
}
}
return 0;
}
The two Statements:
cin.clear();
cin.ignore(INT_MAX,'\n');
are flushing the Input stream until the newline is encountered.
The first answer already explains how to flush the cin stream after the user types in a char.
If you want to determine which character it was, you should define userInput as std::string. If the string is not "q" or "a" or whatever you are looking for, you have to cast the string to a double, just like this:
std::string str;
cin >> str;
if (str == "j")
// User typed in a special character
// ...some code...
else
double d = atof(str.c_str()); // Cast user input to double
Notice that the result of the cast is zero, if the user typed in any other string than the ones you especially look for.

Check if "cin" is a string

I have a simple little script I am coding, and I am trying to not allow people to enter a string, or if they do make it revert to the beginning of the function again. Here's the input code I have:
int main()
{
cout << "Input your first number" << endl;
cin >> a;
cout << "Input your second number" << endl;
cin >> b;
}
The rest of the code beyond this part works just fine for what's going on, although if a string is entered here it obviously doesn't work.
Any help would be appreciated.
You may find this post useful,
How to check if input is numeric in C++
Basically you can check the input, whether it is numeric value or not. After checking whether the given input is numbers, then you can add a while loop in main to ask user to repeat if input is not a valid number.
Every input is a string. If you want to know if a entered string can convert to a number, you have to read in a string and try to convert it yourself (eg with strol).
An alternative would be to check if the reading from cin failed, but personally i don't like it because cin.fail() covers more error situations than just a failed type conversion.
There's a library function may help,you can check it after input:
int isdigit(char c);
Tips:
1.You should include such files :
# include <ctype.h>
2.If c in 0 ~ 9 ,return 1 ; else return 0.

Trying to use a while statement to validate user input C++

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){}