while loop running for every digit/character from input - c++

Hey guys beginner in C++ and coding in general. I am currently making a tictactoe program. For the part of the program I am validating user input. Since it is a 3x3 table, I want to make sure their input is an integer and that they choose a number between 1~9.
To do this I wrote
//Validating user input
void move() {
std::cout << "It's Player" << player << "'s turn!\n";
while(!(std::cin >> position)){
std::cout << "Please choose a NUMBER between 1~9!\n";
std::cin.clear();
std::cin.ignore();
}
while(position < 1 || position > 9){
std::cout << "Please choose a number BETWEEN 1~9!\n";
std::cin.clear();
std::cin.ignore();
}
while(board[position - 1] != " ") {
std::cout << "Already filled please choose another number between 1~9!\n";
std::cin >> position;
}
}
It works but for some reason when I put in an input like 10, it would print Please choose a number BETWEEN 1~9! twice (for each digit) and if I input in for example "apple" it would print Please choose a NUMBER between 1~9! four times (for each character). How do i make it just print out the statement once?
Thank you!

Let me try to explain to you the problem. It is a little bit subtle and not that easy to understand. Both other answers adress only the obvious part.
Then, let us first recap that:
The boolean condition in the while statement is loop invariant. Meaning, it will not be modified within the loop. Whatever it was before the loop, will be the same after the loop body has been executed. The condition will never change.
So, for the case where you enter a wrong number:
If the input number is correct (1..9) and the while statement starts to evaluate the boolean expression, it will be false in this case and the loop will not be entered.
If the number is out of your selected bounds (<1 or >9), then the boolean condition is true. The while loop starts, but the condition relevant variable will not be changed in the loop boody and hence, the boolean expression is always true. The loop will run forever.
Additionally, and now comes the answer to your first question, the following will happen:
The text "Please choose a number BETWEEN 1~9!\n" will be shown (first time)
clear will be called for std::cin. The failbit was not set, but anyway. This does not harm
The ignore function is an unformatted input function. It will actively read the next character from the input buffer, which is the end of line `'\n' character.
We enter again the while statement. The condition is still true (position was not modified in the loop body), and we enter the loop again.
The text "Please choose a number BETWEEN 1~9!\n" will be shown (second time)
clear will be called for std::cin. The failbit was not set, but anyway. This does not harm
The ignore function is a formatted input function. It will actively read the next character from the input buffer. But there is none. So it will wait until a key is pressed. For example "enter". After that, it would go back to number 5.
By the way. If you would now enter "abc" then you would see the text 4 times for a,b,c and enter.
So, please remember: ignore is an input function!
Next. It is important to understand, that if you enter an unexpected value, like "apple" instead of "3", the formatted input function >> can do no conversion and sets the failbit. It will also not extract further wrong characters from the input stream (std::cinis a buffered stream). The characters that could not be converted are still in the buffer and wil be read next time.
Please read here about formatted/unformatted input. And especially read about the extraction operatpr >> here..
There you can read the following:
If extraction fails (e.g. if a letter was entered where a digit is expected), zero is written to value and failbit is set.
OK, understood. Then, what is going on here, if you enter "abc". Basically, the same as above.
Enter abc
The boolean condition !(std::cin >> position)will be evaluated to true, because an 'a' was read and cannot be converted to a number.
The std::cin's failbit will be set. The variable positionwill be set to 0.
"Please choose a NUMBER between 1~9!\n" will be shown
The failbit will be reset
Ignore will extract exactly the one wrong character and discard it
std::cin >> position`` will be called again and extract the next wrong character 'b'. 3., 4., 5., 6. will be done again. Until the last charcter in the buffer, the newline '\n' will be extracted. Then you may enter the next number.
The fix for that problem is simple:
ignore has a parameter, where you can specify, how many characters shall be ignored. So, not only one, but all until the end of line.
You should write:
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
This will ignore all bad input.
And to fix your whole program, you could write at the top:
while (!(std::cin >> position) or (position < 1 ) or ( position > 9)) {
std::cout << "Please choose a NUMBER between 1~9!\n";
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}

while(position < 1 || position > 9){
This while loop will continue running as long as position is less than 1 or greater than 9, that's what this says.
But there's nothing in the while loop itself that changes the value of position. For that simple reason, if position's value at this point is outside of the range of 1-9, this while loop will execute forever.
You always need to keep in mind The Golden Rule Of Computer Programming: your computer always does exactly what you tell it to do instead of what you want it to do. Here, you told your computer to execute this while loop as long as position is less than 1 or greater than 9, so this is what your computer will do until this is no longer the case.

You can change your code as this. With this you run your while loop ones for every input. Until you get the value as you like. If the value is as you wish, you get out of the loop with break
std::cout << "Please choose a NUMBER between 1~9!\n";
while(std::cin >> position){
std::cin.clear();
std::cin.ignore();
if(position < 1 || position > 9){
std::cout << "Please choose a number BETWEEN 1~9!\n";
}else{
break;
}
}

Related

Find out the average value of inputted integers

I managed to get this program to work. If the user types an unfixed amount of integers, the program will calculate the average value of it. But I need to end it with <Ctrl-D> in my terminal (end of file) in order for it to work. Why can I not just press enter for it to work?
I also believe that I've used an unnecessary amount of variables. Can it be narrowed down to maybe 2 variables?
#include <iomanip>
#include <iostream>
using namespace std;
int main ()
{
int digit {};
int res {};
int counter {};
cout << "Type in integers: ";
while (cin >> digit)
{
counter ++;
res += digit;
}
cout << "The mean was " << setw(1) << setprecision(1) << fixed << static_cast<double>(res) / static_cast<double>(counter) << endl;
return 0;
}
Why can I not just press enter for it to work?
Because that's not how the overloaded >> formatted extraction operator works. This operator skips over an unlimited amount of whitespace characters, including newline characters, until it reads the integer. It's simply how it works: it will read newlines and spaces, after newlines, and spaces, until it sees a digit. That's its mission in life: read and skip over spaces and newlines until it reads at least one digit. It never gets tired of reading newlines and spaces, and will keep going as long that's the case.
To handle input in the fashion you describe requires a completely different approach: using std::getline to read a single line of input into a std::string, up until the next newline character. Then, once that's done, you can check if the std::string is empty, which means that no input was entered, and then terminate; otherwise take the input in std::string and convert it to an int value (using std::stoi, std::from_chars, or a std::istringstream -- take your pick), and then proceed with the existing algorithm.
Can it be narrowed down to maybe 2 variables?
How do you expect to do that? Hard, immutable logic dictates that you must keep track of at least two discrete values: the total sum and the number of values read. But then you just ran out of variables. You have no more variables to use for storing the next read value (if there is one), using whatever approach you chose to use. So, you can't do it. Rules of logic require the use of at least three variables, possibly more depending on how fancy and robust you want your input validation to work.

Why is the first digit of a string still read and output in a while loop where the condition !isdigit is false?

Note: I know the solution that would solve the issue, but I don't understand the "computer logic," or what is going on behind the compiler.
In my C++ textbook, there is a blurb of example code that reads:
cout << "Enter a line of input:\n";
char next;
while ((!isdigit (next)) && (next != '\n'))
{
cin.get (next);
cout << next;
}
cout << "<END OF OUTPUT>";
... Paired with the example input: I'll see you at 10:30 AM.
I would expect, after typing in the input, for the output to be I'll see you at <END OF OUTPUT>. Instead, the first digit, "1," is also output, such that it becomes I'll see you at 1<END OF OUTPUT>. Why is this? Is this because the statement cin.get (next); is not declared/initialized outside of the while loop, and thus the first next value tested in the while loop's conditional parameters is not actually the first character of the keyboard input? Or does the while loop run an extra iteration when the next condition is not satisfied?
If it's the former, what does the computer test if next is not set to a value? And why does the first digit ("1") read still meet the condition to run the loop again one more time before terminating?
As mentioned in the comments, you have to initialize next before using it, otherwise it is undefined behavior.
Now lets assume, next is properly initialized to a non-digit character. The condition (!isdigit (next)) && (next != '\n') is checked once when you enter the while loop and every time when you reach the end of the statement in curly braces. In your first version, you get a new char and immediately stream it to cout. The check is done afterwards and the loop terminates as expected.

how to ignore n integers from input

I am trying to read the last integer from an input such as-
100 121 13 ... 7 11 81
I'm only interested in the last integer and hence want to ignore all
previous integers.
I thought of using cin.ignore but that won't work here due to
unknown integers (100 is of 3 digits, while 13 is of 2 digits & so on)
I can input integer by integer using a loop and do nothing with them. Is there a better way?
It all depends on the use case that you have.
Reading a none specified number of integers from std::cin is not as easy at it may seem. Because, in contrast to reading from a file, you will not have an EOF condition. If you would read from a file stream, then it would be very simple.
int value{};
while (fileStream >> value)
;
If you are using std::cin you could try pressing CTRL-D or CTRL-Z or whatever works on your terminal to produce an EOF (End Of File) condition. But usually the approach is to use std::getline to read a complete line until the user presses enter, then put this line into a std::istringstream and extract from there.
Insofar, one answer given below is not that good.
So, next solution:
std::string line{};
std::getline(std::cin, line);
std::istringstream iss{line};
int value{};
while (iss >> value)
;
You were asking
Is there a better way?
That also depends a little. If you are just reading some integers, then please go with above approach. If you would have many many values, then you would maybe waste time by unnecessarily converting many substrings to integers and loose time.
Then, it would be better, to first read the complete string, then use rfind to find the last space in the string and use std::stoi to convert the last substring to an integer.
Caveat: In this case you must be sure (or check with more lines of code) that there are no white space at the end and the last substring is really a number. That is a lot of string/character fiddling, which can most probably avoided.
So, I would recommend the getline-stringstream approach.
You can try this simple solution for dynamically ignoring rest of the values except the last given in this problem as shown:
int count = 0;
int values, lastValue; // lastValue used for future use
std::cout << "Enter your input: ";
while (std::cin >> values) {
lastValue = values; // must be used, otherwise values = 0 when loop ends
count++;
}
std::cout << lastValue; // prints
Note: A character must be required to stop the while(), hence it's better put a . at last.
Output example
Enter your input: 3 2 4 5 6 7.
7
Try this:
for( int i=0; i<nums_to_ignore; i++) {
int ignored;
std::cin >> ignored;
}

End array input with a newline?

Not sure if the title is properly worded, but what I am trying to ask is how would you signify the end of input for an array using newline. Take the following code for example. Not matter how many numbers(more or less) you type during the input for score[6], it must take 6 before you can proceed. Is there a method to change it so that an array can store 6 or 100 variables, but you can decide how many variables actually contain values. The only way I can think of doing this is to somehow incorporate '\n', so that pressing enter once creates a newline and pressing enter again signifies that you don't want to set any more values. Or is something like this not possible?
#include <iostream>
using namespace std;
int main()
{
int i,score[6],max;
cout<<"Enter the scores:"<<endl;
cin>>score[0];
max = score[0];
for(i = 1;i<6;i++)
{
cin>>score[i];
if(score[i]>max)
max = score[i];
}
return 0;
}
To detect "no input was given", you will need to read the input as a input line (string), rather than using cin >> x; - no matter what the type is of x, cin >> x; will skip over "whitespace", such as newlines and spaces.
The trouble with reading the input as lines is that you then have to "parse" the input into numbers. You can use std::stringstream or similar to do this, but it's quite a bit of extra code compared to what you have now.
The typical way to solve this kind of problem, however, is to use a "sentry" value - for example, if your input is always going to be greater or equal to zero, you can use -1 as the sentry. So you enter
1 2 3 4 5 -1
This would reduce the amount of extra code is relatively small - just check if the input is -1, such as
while(cin >> score[i] && score[i] >= 0)
{
...
}
(This will also detect end-of-file, so you could end the input with CTRL-Z or CTRL-D as appropriate for your platform)

Taking inputs if number of inputs is not known

I wish to take inputs from console where the number of inputs are not known. All i know is that they are less then or equal to 10.
I wrote this code but it evaluates the last value twice.
int x;
do{
cin>>x;
cout<<check(x)<<"\n";
}while(std::cin);
The inputs are in this form:
12
2
45
As #LightnessRacesinOrbit commented, it's while(!eof) bug again. You should check the stream state after reading from it, not before.
You can use cin>>x in the while-condition and apply a special value (i.e. anything not a number) as the end flag like this:
while (cin >> x)
{
// got a value
}
You may use istream::geline.
char input[1024]
std::cin.getline (input,1024);
After you read the line, you have to split it by white-spaces as delimiter. Just ignore 11th split onwards. And you may apply atoi or similar functions on the splits (some_split.c_str()) to get the numeric values