Getline() combined with cin.ignore() is not storing variable? (Bug) - c++

I don't think this question is not a duplicate of this. Even though it is caused by the same thing, it is a different manifestation of the problem.
The terminal output when I run this program looks like this (my input is 1 and Rock Lee followed by enter):
Enter a number: 1
Enter your name: Rock Lee
Name is blank.
However, the name shouldn't be blank. Why is the name variable ""? How do I fix this bug? Here is the code:
#include <iostream>
#include <iomanip>
#include <string>
using namespace std;
int main () {
int num;
string name;
cout << "Enter a number: ";
cin >> num;
cout << "Enter your name: ";
getline(cin, name, '\n');
cin.ignore (1000, '\n');
if (name == "")
{
cout << "Name is blank.";
return -1;
}
return 0;
}
Also, please note that I want to use Getline() in the solution, and I want to be able to read an entire line (so the name can be anything, not just first and last name).
When I tried commenting out cin.ignore(1000, '\n');, it gave this output which is also incorrect:
Enter a number: 1
Enter your name: Name is blank.
It doesn't even give me a chance to type in my name. How do I fix this?

cin >> num reads until it sees whitespace, but it doesn't discard the whitespace, so it leaves '\n' in the input. Your call to getline sees it and immediately returns, filling name with an empty string. You need to call cin.ignore before getline to ignore the '\n' that cin::operator>> left laying around.

Related

C++ --- getline, and cin ignore () .deleting first characters in strings on output

#include <iostream>
#include <string>
using namespace std;
struct UserInfo{
string userPhoneNumber;
string userName ;
string userAddress ;
};
int main ()
{
cout << "How many Entries do you want to enter?";
int userAmountSelection;
cin >> userAmountSelection;
UserInfo userOne [userAmountSelection];
for (int i = 0; i < userAmountSelection; i++){
cout << "Please enter your first and last name: ";
cin.ignore(); // possible problem in code
getline (cin, userOne[i].userName, '\n');
cout << "Please Enter your address, " << userOne[i].userName << ":";
cin.ignore(); // possible problem in code
getline (cin, userOne[i].userAddress, '\n');
cout << "Please enter your phone#: ";
cin.ignore (); // possible problem in code
getline (cin, userOne[i].userPhoneNumber);
}
for (int i = 0; i < userAmountSelection; i++){
cout << userOne[i].userName << " " <<
userOne[i].userAddress << " " <<
userOne[i].userPhoneNumber << endl;
}
return 0;
}
As you can see its a simple code for learning structs and experimenting. The problem i run into appears to be from cin.ignore () code. it ignores the first characters of the input strings on output. The code does compile, however input and output are skewed.
For example when i enter a name of Terry, it will output erry.
I have tried removing the cin.ignore (), however when i do that, the output skips over the parts where the user needs to enter data and leaves areas blank.
I have scoured the forums and found suggestions such as adding an argument to the cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');. for example, however this does not solve the problem and only adds to the list of errors I'm experiencing.
The Problem
The problem is with the placement of ignores to prevent the bug outlined in Why does std::getline() skip input after a formatted extraction? The ignores have been placed before the getlines, and while this solves the getline skipping problem, it causes the problem the Asker has encountered. There isn't always something that needs to be ignored.
For example
cin >> userAmountSelection;
will leave a line ending in the stream if the user typed in the amount and then hit enter.
cout << "Please enter your first and last name: ";
cin.ignore(); // possible problem in code
getline (cin, userOne[i].userName, '\n');
inside the for loop Would trip over this line ending if not for the ignore. But getline does not leave a newline in the stream , so the second and subsequent iterations of the loop have no newline to ignore. Part of the requiured data is ignored instead.
After
cin >> userAmountSelection;
rather than before
getline (cin, userOne[i].userName, '\n');
would be a good place to place an ignore so the newline is removed from the stream only after it has been left in the stream, but...
The Solution
The best way to handle this is to always read entire lines with getline and then parse those lines (see option 2 of this answer) into the pieces you want.
std::string line;
std::getline(std::cin, line);
std::istringstream(line) >> userAmountSelection;
This always works (Note: Requires #include <sstream>) and now you only have one type of reading going on, not a game of mix-n-match where you may forget you need an ignore.
Feel free to stop reading now.
The ignore approach requires some extra smarts. It's not as simple as it looks, in addition to fallibility of the human memory. You should place ignores AFTER an operation that leaves unwanted stuff in the stream. If you ignore BEFORE an operation, you often find yourself losing data you wanted because there was nothing to ignore.
std::cin >> userAmountSelection; // read a number
std::cin.ignore(); // discard the next character
Works a lot of the time, but what if the user typed in the amount and then a space and then hit enter or typed in all of the input they needed to type because they new darn well what the next prompt was? You gotta get a bit craftier.
std::cin >> userAmountSelection;
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
This ignore annihilates everything until it hits the end of the line or runs out of space in the stream. (Note: Requires #include <limits>)
The problem i run into appears to be from cin.ignore () code. it ignores the first characters of the input strings on output.
Looking at the istream::ignore declaration
istream& ignore (streamsize n = 1, int delim = EOF);
It will discard the first character by default.
I have tried removing the cin.ignore (), however when i do that, the
output skips over the parts where the user needs to enter data and
leaves areas blank.
That's because of the std::cin performed which left a residual newline which was then consumed by the getline. You'll only need ignore between the std::cin and getline calls:
std::cin >> userAmountSelection;
std::cin.ignore(); //---
...
for (int i = 0; i < userAmountSelection; i++) {
...
getline (...);
...
getline (...)
}
Also, there may be instances where you don't know if an std::cin will precede the getline. In that case, you may check if the input stream contains new line before doing ignore:
...
// some I/O operations happening here
...
// ignore if there's white space
if (std::iswspace(std::cin.peek())) std::cin.ignore();
std::getline(std::cin, somestring);

Problem with string input validation (C++)

I ran into a problem while trying to validate my user input. Here's my code:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string choice;
cout << "Please type something: ";
cin >> choice;
cin.ignore();
while (choice != "1" && choice != "2" && choice != "3" && choice != "4" && choice != "5")
{
cout << "Incorrect choice! Please input a valid number: >_";
cin >> choice;
cin.ignore();
}
return 0;
}
When I input "wronginput", that input fell in the while loop and displayed
Incorrect choice! Please input a valid number: >_
Which is good. However, when I try "wrong input", I get a "weird" version of it:
Incorrect choice! Please input a valid number: >_ Incorrect choice! Please input a valid number: >_
My guess is that the space in between is the culprit. Is there any way for me to fix this? Thank you.
When you enter "wrong input" for input, the line
cin >> choice;
reads and stores "wrong" in choice.
The line
cin.ignore();
ignores only one character. Hence, you stull have "input" and the subsequent newline character in the stream. Next time you read into choice, you get "input" in choice.
That explains the behavior of your program.
In order to fix it, make sure to ignore the rest of the line instead of just one character. Use
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
Add
#include <limits>
to be able to use std::numeric_limits.
That's because std::cin >> choice is a formatted input that will read everything to the next whitespace.
When you type in "wronginput", it reads it all, but if you type in "wrong input", only the "wrong" part will be read. Your while loop will fire, perform std::cin >> choice inside its body once again and that will read "input".
This behaviour causes your program to output the message twice in a row.
How to fix it? Instead of reading a single token, up to the next whitespace, consider reading an entire line.
How to achieve that? Simply instead of using
std::cin >> choice;
use a std::getline function:
std::getline(std::cin, choice);
both outside and inside your while loop.

String not working with #include <string> and using namespace std

about the code below, string doesn't light up anymore and when I entered "John Smith", only "John" appears, string was working fine for me weeks ago until i tried calling strings function today which didn't work so i tested for a simpler one.
#include <iostream>
#include <string>
using namespace std;
int main ()
{
string name;
// Get the user's name
cout << "Please enter your first name: ";
cin >> name;
// Print the greeting
cout << "Hello, " << name << "." << endl;
return 0;
}
string doesn't light up like int
I might be asking at the wrong place but I cant' tell what's the problem, please help :(
To get all the line, use getline(cin, name);
instead of cin >> name;
See http://www.cplusplus.com/reference/string/string/getline/
With std::string's, using std::cin >> someString will only read the first word off the buffer (it will stop at the first whitespace encountered).
Use getline(std::cin, someString) instead to read the entire line.
std::cin gets only characters to first 'white' character, like space, tab or enter.
If you want to read whole line use e.g. getline()
string line;
cin.clear(); //to make sure we have no pending characters in input buffer
getline(cin, line);

How do I get user inputs for a string and then an int?

I have a database class that is an array that will hold a number of objects.
The function will take a couple of inputs from the user which include both strings and ints
For example:
std::cout << "Enter first name: ";
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::getline(std::cin, first_name);
std::cout << "Enter last name: ";
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::getline(std::cin, last_name);
std::cout << "Enter age: ";
std::cin >> age;
When I run the code, after I hit enter after entering the last name, it just starts a new line and I have to enter another input before it asks for the age input.
I heard it was bad to mix getline and cin, and that it's better to use one or the other. What can I do to make this work and what would be good practice moving forward?
Edit: I added in the ignores when I initially searched for solutions because without them, the code wouldn't bother waiting for user input. The output would be "Enter first name: Enter last name: "
Edit2: RESOLVED. Problem was I had used "cin >>" earlier in my code for user to input an int variable and needed the first cin.ignore statement, but not the other. Did not include that part of the code because I didn't know that was affecting it. Still new to all this so thanks everyone for their help!
Your std::cin::ignore calls are not helping you. They are only needed after an input that does not extract the end-of-line character (>>).
std::string first_name;
std::string last_name;
int age;
std::cout << "Enter first name: ";
std::getline(std::cin, first_name); // end of line is removed
std::cout << "Enter last name: ";
std::getline(std::cin, last_name); // end of line is removed
std::cout << "Enter age: ";
std::cin >> age; // doesn't remove end of line
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // this does
// input can proceed as normal
You only need the std::cin::ignore call after std::cin >> age; because that doesn't remove the end of line character whereas the std::getline calls do.
According to the documentation of std::basic_istream::ignore(), this function behaves as an Unformatted Input Function which mean it is going to block and wait for user input if there is nothing to skip in the buffer.
In your case both of your ignore statments are not neccessary since std::getline() will not leave the new line character in the buffer. So what is actually happening is:
std::cout << "Enter first name: ";
/*your first input is skipped by the next ignore line because its going to block until
input is provided since there is nothing to skip in the buffer*/
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
/* the next getline waits for input, reads a line from the buffer and also removes the
new line character from the buffer*/
std::getline(std::cin, first_name);
std::cout << "Enter last name: ";
/*your second input is skipped by the next ignore line because its going to block until
input is provided since there is nothing to skip in the buffer*/
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
/* the next getline waits for input and this is why it seems you need to provide
another input before it ask you to enter the age*/
std::getline(std::cin, last_name);
You need to remove the ignore statments to make this work. You may also want to read When and why do I need to use cin.ignore() in C++
I recommend removing the ignore function calls:
std::string name;
std::cout << "Enter name: ";
std::getline(cin, name);
unsigned int age;
std::cout << "Enter age: ";
std::cin >> age;

getline(cin) behaving unexpectedly (c++)

I have the following function:
void playersetup(string &name){
char verify;
cout<<"Enter player name: ";
getline(cin,name);
cout<<endl<<"Your name is: " + name + ", is that correct? (Y for yes, any other key for no): ";
cin>>verify;
if ((verify=='y')||(verify=='Y')) {
verify=='n';
} else {
playersetup (name);
}
}
It asks for the player's name, asks the user to verify thats the name they intended, if not it calls itself to start over. However this is what the output looks like:
Enter Player Name:
user enters: John Smith
Your name is: John Smith, is that correct? (Y for yes, any other key for no):
user enters: n
Enter Player Name:
(user is not given a chance to input anything)
Your name is: , is that correct? (Y for yes, any other key for no):
Is there logic error here or an aspect of getline(cin) that Im missing?
The newline character remains in standard input as the >> char consumes the users single character response only. When std::getline() is next invoked it reads the newline character; a blank line. Consume, or skip, the newline character before asking the user for the name again:
#include <limits> // for 'std::numeric_limits'
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n')
If you're using std::getline after std::cin >> verify you need to flush the newline '\n' out of the buffer in between. Otherwise getline reads '\n' and returns. You can add a call to std::basic_istream::ignore to ignore '\n':
#include <limits>
cin>>verify;
cin.ignore ( std::numeric_limits<std::streamsize>::max(), '\n');
basic_istream/ignore