getline() function reading in whitespace before input is entered - c++

Alright, I wrote a game of hangman. The game works great, except after the user finishes the game, and enters the char value of Y to play again.
I have traced the problem down to the getline() function at the start of my do-while loop. If I enter Y, then the do-while loop succesfully repeats, but the getline function seems to already think that there is input in cin, even though I don't enter anything.
Here is my code so far:
#include<iostream>
#include<string>
using namespace std;
int main() {
string secretWord;
string secretWordClean = "";
string guessedLetters; //to be loaded with _ characters equal to length of secretWord
string incorrectlyGuessedChars = "";
char individualCharGuess;
char playAgain;
size_t countOfLetters = 0; //begine count at 0
size_t guessesRemaining;
int guessedUsed;
begin_game://label which we can use to bring us back to the start of the do-while loop at any time
do{//start of the game
cout << "Please enter a secret word: ";
getline(cin, secretWord); //y getline is cuaing the issue
for(int i = 0; i < secretWord.length(); i++){
if (isalpha(secretWord[i])){
secretWordClean += secretWord[i];
}
}
secretWord = secretWordClean; //assign all alpha secret word string back to original variable for better readability
guessesRemaining = secretWord.length() * 2;
for(int i = 0; i < secretWord.length(); i++){
guessedLetters += "_"; //fills guessedLetters with blanks equal to the length of the secretWord
}
cout << "Please guess a letter, you have " << guessesRemaining << " guesses remaining!" << endl;
cin >> individualCharGuess;
for(int i = 0; i < secretWord.length(); i++){ //every complete iteration of this for loop = one single guess
if(secretWord[i] == individualCharGuess){
guessedLetters[i] = individualCharGuess; //will replace the spaces with the correct character, if guessed
countOfLetters++; //if any letter is guessed correctly, this indicator will be inrimented above 0
continue;
}
if(secretWord.find(individualCharGuess) == string::npos){
if(incorrectlyGuessedChars.find(individualCharGuess) == string::npos){
incorrectlyGuessedChars += individualCharGuess;
}
}
}
if(secretWord.compare(guessedLetters) == 0){
cout << "You win! The word was: " << secretWord << endl;
guessedUsed = ((secretWord.length() * 2) - guessesRemaining) + 1 ;
cout << "You used " << guessedUsed << " guesses." << endl;
cout << "Play again? Enter Y for Yes, or anything else to exit: ";
cin >> playAgain;
if(playAgain != 'Y'){
break; //exit the loop if user guesses all the letters and doesn't want to play again
}
else {
goto begin_game;
}
}
guessesRemaining--; //we decriment our total guesses remaining if the user does not win the game or run out of guesses
if(countOfLetters > 0){
cout << "You have correctly guessed a letter!" << endl;
cout << "Here are the letters you have guessed correctly so far: ";
cout << guessedLetters << endl;
cout << "Here are the letters you have guessed incorrectly so far: ";
cout << incorrectlyGuessedChars << endl;
countOfLetters = 0; //reset the counter to prepare for next iteration of do-while loop
}
else if (guessesRemaining <= 0) {
cout << "You have run out of guesses!" << endl;
cout << "Here are the letters that you guessed correctly: ";
cout << guessedLetters << endl;
cout << "Here are the letters you guessed incorrectly: ";
cout << incorrectlyGuessedChars << endl;
cout << "The secret word was: " << secretWord << endl;
cout << "Play again? Enter Y for Yes, or anything else to exit: ";
cin >> playAgain;
if(playAgain != 'Y'){
break; //exit the loop if user guesses all the letters and doesn't want to play again
}
else goto begin_game;
}
else {
cout << "You guessed wrong! Keep trying, " << guessesRemaining << " guesses to go!" << endl;
cout << "Here are the letters you have guessed correctly so far: ";
cout << guessedLetters << endl;
cout << "Here are the letters you have guessed incorrectly so far: ";
cout << incorrectlyGuessedChars << endl;
}
}while (secretWord.compare(guessedLetters) != 0 || guessesRemaining != 0); //use to repeat the request for a single char guess
return 0;
}

You are mixing formatted and unformatted I/O: reading a character will stop immediately after reading the character. Since after entering the character you entered a newline the newline still sticks in the stream, read for getline() to terminate the line. You should skip leading whitespace before using std::getline(), e.g.:
if (std::getline(std::cin >> std::ws, s)) {
...
}
Alternatively, you could use ignore() to ignore all characters up to and including the newline. Note that ignoring just one character won't work reliably as a sequence of spaces between the '\n'. To use ignore() you should use the proper magic number instead:
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
BTW, you should also always verify that input was actually successful as you'd otherwise easily get incorrect behavior when it isn't.

Don't mix token extraction using >> and line extraction using getline. The former doesn't remove newlines, so the next getline call after token extraction may end up reading the remaining bit of the previous line, which may be an empty string.
If you must mix the two kinds of input, use std::cin >> std::ws to gobble up stray whitespace (including the newline) before using getline.

Related

How to inverse string without library functions - C++

I'm having trouble with understanding how to output the inverse of the string then store the inverse value and apply it to a string variable to print the string later on. Some errors I receive depending on the structure of the program include : random characters being printed, just the string input being printed, the programming crashing... The program must be able to process this menu:
At one point I had the inverse working, but when I would add for example if(choice == '2') and create the reverse function it would start acting up again.
This is what I have for now :
int main()
{
string userString = "";
int i = 0;
char choice;
char invChar = char(userString[i]);
userString = invChar;
cout << "Please enter a word, a sentence, or a string of numbers." << endl;
getline(cin, userString);
do
{
cout << "\nUSE THIS MENU TO MAINPULATE YOUR STRING\n"
<< "----------------------------------------" << endl;
cout << "1) Inverse String\n"
<< "2) Reverse String\n"
<< "3) To Uppercase\n"
<< "4) Count Number Words\n"
<< "5) Count Consonants\n"
<< "6) Enter a Different String\n"
<< "7) Print the String\n"
<< "Q) Quit" << endl;
cin >> choice;
cin.ignore();
if (choice == '1')
{
for (int i = 0; i <= userString.length(); ++i)
{
if (isupper(userString[i]))
{
char(tolower(userString[i])); // if uppercase - converts to lower - if upper keeps value
invChar += userString[i];
}
userString = invChar;
}
cout << userString;
}
} while (choice != 'q' || choice != 'Q');
Thanks in advance for any tips and tricks!
Did you maybe mean to do this:
for (int i = 0; i < userString.length(); ++i)
{
if (isupper(userString[i]))
{
userString[i] = (tolower(userString[i]));
}
else if(islower(userString[i]))
{
userString[i] = (toupper(userString[i]));
}
}
This will actually inverse. What you were doing before doesn't work, and also only converts to uppercase

Program ignores the first letter of input

I've seen other questions about this problem but I can't seem to incorporate those in my problem right here.
My Code
cout << "Get User's input\n";
for (int i = 0; i < size; i++)
{
cout << "Enter the author's name: ";
cin.ignore();
getline(cin,a[i].name); // Ariel the Mermaid
cout << endl;
for (int count = 0; count < size; count++)
{
cout << "Enter Title " << count + 1 << " : ";
cin.ignore();
getline(cin,a[i].books[count].title); // Intro to Me
if (a[i].books[count].title == "NONE")
break;
cout << "Enter Price " << count + 1 << " : $";
cin >> a[i].books[count].price; // 49.99
}
cout << endl;
}
When I have inputted "Ariel the Mermaid", it gives me "riel the Mermaid" when I cout it. Also the "Intro to Me" gives "ntro to Me".
What's the problem with this code?
You are skipping the first character with cin.ignore().
A good use of cin.ignore() is when you have cin >> before a getline()
for example:
cout << "Digit you age" << endl;
cin >> age;
cout << "Digit your full Name" << endl;
cin.ignore();
getline(cin,name);
This happens because when the compiler reachs the cin will make a stop to read from the keyboard until you pressed the enter key to finish, then the getline() will capture the last character wich is a "\n". So you have to cin.ignore() to ignore that last character.
Your call to cin.ignore() is ignoring one character. This method ignores a count of n characters provided as an argument. The default n is 1.
You should delete the line with cin.ignore ().

How come the first letter in my final cout gets cut off?

int main () {
const int MAX_INPUT = 99;
string names[MAX_INPUT];
string eraseName;
string newList;
int numNames = 0;
int i = 0;
cout << "How many names do you want (max 99)? ";
cin >> numNames;
do {
if(numNames > MAX_INPUT) {
cout << "Out of memory!" << endl;
break;
}
cout << "Enter name #" << (i+1) << ": ";
cin.ignore();
getline(cin,names[i]);
++i;
}
while (i < numNames);
cout << "What name do you want to eliminate? ";
getline(cin,eraseName);
cout << "Here is the list in reverse order, skipping ";
cout << eraseName << "..." << endl;
i = 0;
for (i = 0; i < numNames; ++i) {
cout << names[i] << endl;
}
return 0;
}
I have an assignment where I have to "eliminate" an element in an array and recreate the output. I know my final for loop will not erase the element, it's just there because I was testing the issue, but if names has two inputs (John Doe and Jane Doe) and I say to cout them the final loop couts:
John Doe
ane Doe
Move cin.ignore() right after cin >> numNames;, before the loop that reads the names.
You only need this to ignore the newline that is left in the stream after reading the number of names. getline() reads (and ignores) the newline from the stream, so there's no need to call ignore() again before reading each name. As a result, it's reading and ignoring the first character of the name.
The following block of code
if(numNames > MAX_INPUT) {
cout << "Out of memory!" << endl;
break;
}
does not need to be executed in every iteration of the do-while loop. You can change you function to use:
if(numNames > MAX_INPUT) {
cout << "Out of memory!" << endl;
// Deal with the problem. Exit??
}
do {
cout << "Enter name #" << (i+1) << ": ";
cin.ignore();
getline(cin,names[i]);
++i;
} while (i < numNames);
Once you move the check out of the loop, you have to ask yourself, "Do I need to ignore a character in each iteration of the loop?" The answer is "No". You need to ignore the newline only after reading numNames. So, you move it out of the loop also.
if(numNames > MAX_INPUT) {
cout << "Out of memory!" << endl;
// Deal with the problem. Exit??
}
// Ignore the newline left on the stream before reading names.
cin.ignore();
do {
cout << "Enter name #" << (i+1) << ": ";
getline(cin,names[i]);
++i;
} while (i < numNames);
You can improve on that by making sure that everything up to and including the newline is ignored by using:
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
Add
#include <limits>
to be able to use std::numeric_limits.

Code to get user input not executing/skipping in C++

In the below code, I'm running into an error when I try to get the user to input their name. My program just skips it over and goes right over to making the function calls without allowing the user to enter their name. Despite the error, my program is compiling. I'm not sure what's going wrong as I wrote that part based off other examples I found on here. Any suggestions?
#include <iostream>
#include <string>
#include <time.h>
using namespace std;
char showMenu();
void getLottoPicks(int[]);
void genWinNums(int[]);
bool noDuplicates(int[]);
const int SIZE = 7;
int main()
{
int userTicket[SIZE] = {0};
int winningNums[SIZE] = {0};
char choice;
string name;
srand(time(NULL));
do
{
choice = showMenu();
if (choice == '1')
{
cout << "Please enter your name: " << endl;
getline(cin, name);
getLottoPicks(userTicket);
genWinNums(winningNums);
for (int i = 0; i < SIZE; i++)
cout << winningNums[i];
}
} while (choice != 'Q' && choice != 'q');
system("PAUSE");
return 0;
}
Added the code for showMenu:
char showMenu()
{
char choice;
cout << "LITTLETON CITY LOTTO MODEL:" << endl;
cout << "---------------------------" << endl;
cout << "1) Play Lotto" << endl;
cout << "Q) Quit Program" << endl;
cout << "Please make a selection: " << endl;
cin >> choice;
return choice;
}
And getLottoPicks (this part is very wrong and I'm still working on it):
void getLottoPicks(int numbers[])
{
cout << "Please enter your 7 lotto number picks between 1 and 40: " << endl;
for (int i = 0; i < SIZE; i++)
{
cout << "Selection #" << i + 1 << endl;
cin >> numbers[i];
if (numbers[i] < 1 || numbers[i] > 40)
{
cout << "Please choose a number between 1 and 40: " << endl;
cin >> numbers[i];
}
if (noDuplicates(numbers) == false)
{
do
{
cout << "You already picked this number. Please enter a different number: " << endl;
cin >> numbers[i];
noDuplicates(numbers);
} while (noDuplicates(numbers) == false);
}
}
}
After doing cin >> choice; inside char showMenu(), if a user inputs 1[ENTER], the char consumes 1 character from cin, and the newline stays inside the stream. Then, when the program gets to getline(cin, name);, it notices that there's still something inside cin, and reads it. It's a newline character, so getline gets it and returns. That's why the program is behaving the way it is.
In order to fix it - add cin.ignore(); inside char showMenu(), right after you read the input. cin.ignore() ignores the next character - in our case, the newline char.
And a word of advice - try not to mix getline with operator >>. They work in a slightly different way, and can get you into trouble! Or, at least remember to always ignore() after you get anything from std::cin. It may save you a lot of work.
This fixes the code:
char showMenu()
{
char choice;
cout << "LITTLETON CITY LOTTO MODEL:" << endl;
cout << "---------------------------" << endl;
cout << "1) Play Lotto" << endl;
cout << "Q) Quit Program" << endl;
cout << "Please make a selection: " << endl;
cin >> choice;
cin.ignore();
return choice;
}
from looking at code showMenu function has problem. and it's not returning asccii equivalent of '1' that is: 31 integer. try printing value returned by showmenu. you will get that
UPDATE:
It is because cin in delimited by ' '(whitespace) and getline by '\n' character, so when enter name and press enter cin in showmenu will consume whole string except '\n' from istream and that is read by getline. to see this when it ask for choice enter string like 1 myname (1 whitespace myname)and press ENTER will display name. now cin will read 1 in choice and myname in name by getline.

Using a space for canceling a simple loop

Here is my code for this simple assignment:
Write the code that will read character input from the user until a blank (a space) is entered. Print how many characters were entered. Keep in mind the user may decide to enter a blank as his first character.
Why is the space not ending the loop?
#include <iostream>
using namespace std;
int main(){
char answer;
int count=1;
do{
cout << "please enter number " << count;
cin >> answer;
count++;
}while(answer!=' ');
cout << "you entered " << count-1 << "numbers." << endl;
return 0;
}
The cin >> operations skip all kinds of whitespace by default. You can use cin >> noskipws; before your loop to disable whitespace skipping or use cin.get() instead:
cin.get(answer);
You should be aware that newlines and carriage returns are no longer skipped now, so you have to handle them separately. Also, you should check the stream status to react to end-of-file:
do {
cout << "Please enter number " << count << endl;
do {
cin.get(answer);
} while (cin && (answer == '\r' || answer == '\n'));
count++;
} while (cin && answer != ' ');