Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does std::getline() skip input after a formatted extraction?

People also ask

Why does C++ Getline skip?

It is not the getline() function skipping, cin in responsible for it. When you take any input using cin, it stores '\n' in buffer. Since, getline does not ignore leading whitespace, it thinks it has finished reading and stops reading any further. So, this problem arises when getline() is used after cin statement.

Why Getline is not working after CIN?

The getline() function in C++ is used to read a string or a line from the input stream. The getline() function does not ignore leading white space characters. So special care should be taken care of about using getline() after cin because cin ignores white space characters and leaves it in the stream as garbage.

Does Getline skip newline?

getline(cin, newString); begins immediately reading and collecting characters into newString and continues until a newline character is encountered. The newline character is read but not stored in newString.

Does Getline wait for input?

std::cin leaves the newline character in the buffer after pressing enter, and getline just grabs it and keeps going, that's why getline doesn't block to wait for input.


Why does this happen?

This has little to do with the input you provided yourself but rather with the default behavior std::getline() has. When you provided your input for the age (std::cin >> age), you not only submitted the following characters, but also an implicit newline was appended to the stream when you typed Enter:

"10\n"

A newline is always appended to your input when you select Enter or Return when submitting from a terminal. It is also used in files for moving toward the next line. The newline is left in the buffer after the extraction into age until the next I/O operation where it is either discarded or read. When the flow of control reaches std::getline(), it will see "\nMr. Whiskers" and the newline at the beginning will be discarded, but the input operation will stop immediately. The reason this happens is because the job of std::getline() is to attempt to read characters and stop when it finds a newline. So the rest of your input is left in the buffer unread.

Solution

cin.ignore()

To fix this, one option is to skip over the newline before doing std::getline(). You can do this by calling std::cin.ignore() after the first input operation. It will discard the next character (the newline character) so that it is no longer in the way.

std::cin >> age;
std::cin.ignore();
std::getline(std::cin, name);

Match the operations

When you run into an issue like this it's usually because you're combining formatted input operations with unformatted input operations. A formatted input operation is when you take input and format it for a certain type. That's what operator>>() is for. Unformatted input operations are anything other than that, like std::getline(), std::cin.read(), std::cin.get(), etc. Those functions don't care about the format of the input and only process raw text.

If you stick to using a single type of formatting then you can avoid this annoying issue:

// Unformatted I/O
std::string age, name;
std::getline(std::cin, age);
std::getline(std::cin, name);

or

// Formatted I/O
int age;
std::string first_name, last_name;
std::cin >> age >> first_name >> last_name;

If you choose to read everything as strings using the unformatted operations you can convert them into the appropriate types afterwards.


Everything will be OK if you change your initial code in the following way:

if ((cin >> name).get() && std::getline(cin, state))

This happens because an implicit line feed also known as newline character \n is appended to all user input from a terminal as it's telling the stream to start a new line. You can safely account for this by using std::getline when checking for multiple lines of user input. The default behavior of std::getline will read everything up to and including the newline character \n from the input stream object which is std::cin in this case.

#include <iostream>
#include <string>

int main()
{
    std::string name;
    std::string state;

    if (std::getline(std::cin, name) && std::getline(std::cin, state))
    {
        std::cout << "Your name is " << name << " and you live in " << state;
    }
    return 0;
}
Input:

"John"
"New Hampshire"

Output:

"Your name is John and you live in New Hampshire"

Since everyone above has answered the problem for input 10\nMr Whisker\n, I would like to answer a different approach. all the solution above published the code for if the buffer is like 10\nMr Whisker\n. but what if we don't know how user will behave giving input. the user might type 10\n\nMr. Whisker\n or 10 \n\n Mr. whisker\n by mistake. in that case, codes above may not work. so, I use the function below to take string input to address the problem.

string StringInput()  //returns null-terminated string
{
    string input;
    getline(cin, input);
    while(input.length()==0)//keep taking input until valid string is taken
    {
        getline(cin, input);
    }
    return input.c_str();
}

So, the answer would be:

#include <iostream>
#include <string>

int main()
{
    int age;
    std::string name;

    std::cin >> age;
    name = StringInput();
    
    std::cout << "My cat is " << age << " years old and their name is " << name << std::endl;
    
}

Extra:

If user inputs a \n10\n \nmr. whiskey; To check whether int input is valid or not, this function can be used to check int input (program will have undefined behavior if char is given as input instead of int):


//instead of "std::cin>>age;" use "get_untill_int(&age);" in main function.
void get_Untill_Int(int* pInput)//keep taking input untill input is `int or float`
{
    cin>> *pInput;
    /*-----------check input validation----------------*/
    while (!cin) 
    {
        cin.clear();
        cin.ignore(100, '\n');
        cout<<"Invalid Input Type.\nEnter again: ";
        cin >>*pInput;
    }
    /*-----------checked input validation-------------*/
}

I am really wondering. C++ has a dedicated function for eating up any remaining or whatever white spaces. It is called std::ws. And then, you can simply use

std::getline(std::cin >> std::ws, name);

That should be the idomatic approach. For each transistion between formatted to unformatted input taht should be used.