When and why do I need to use cin.ignore() in C++?
Asked Answered
P

7

97

I wrote a very basic program in C++ which asked the user to input a number and then a string. To my surprise, when running the program it never stopped to ask for the string. It just skipped over it. After doing some reading on StackOverflow, I found out that I needed to add a line that said:

cin.ignore(256, '\n');

before the line that gets the string input. Adding that fixed the problem and made the program work. My question is why does C++ need this cin.ignore() line and how can I predict when I will need to use cin.ignore()?

Here is the program I wrote:

#include <iostream>
#include <string>

using namespace std;

int main()
{
    double num;
    string mystr;

    cout << "Please enter a number: " << "\n";
    cin >> num;
    cout << "Your number is: " << num << "\n";
    cin.ignore(256, '\n'); // Why do I need this line?
    cout << "Please enter your name: \n";
    getline (cin, mystr);
    cout << "So your name is " << mystr << "?\n";
    cout << "Have a nice day. \n";

}
Polysyllable answered 24/8, 2014 at 19:17 Comment(4)
@LightnessRacesinOrbit vote to close as a duplicate then?Tamelatameless
@LightnessRacesinOrbit Sorry about duplicating the question.Polysyllable
@MattMcNabb: Yeah but cba to find it. Would have been nice if I didn't have to.Anoxemia
Okay, so when I run your code without ignore, it skips right past the second cin>>, presumably because the endl is still in the stream. But when I run your code with ignore, it still skips past the second cin>> if I type anything other than numeric characters. And if I put in two ignores, it waits for a third input before the second cin>>. Can anyone explain this?Cap
P
95

ignore does exactly what the name implies.

It doesn't "throw away" something you don't need. Instead, it ignores the number of characters you specify when you call it, up to the char you specify as a delimiter.

It works with both input and output buffers.

Essentially, for std::cin statements you use ignore before you do a getline call, because when a user inputs something with std::cin, they hit enter and a '\n' char gets into the cin buffer. Then if you use getline, it gets the newline char instead of the string you want. So you do a std::cin.ignore(1000,'\n') and that should clear the buffer up to the string that you want. (The 1000 is put there to skip over a specific number of chars before the specified delimiter, in this case, the '\n' newline character.)

Pender answered 24/8, 2014 at 20:46 Comment(5)
Thanks. Would it be too much to ask why C++ has the problem of getting a '\n' char in the the cin buffer as you described? Is that a flaw in the language? Or was it intentionally made that way because it serves some purpose?Polysyllable
Not exactly a flaw, more like precision. Technically, if the newline character doesn't get read in, then it isn't reading everything. Input buffers ignore whitespace, so when you are reading in information from std::cin, the newline doesn't matter.. But when youre talking about getline, it gets the entire line up to the newline character, and when the newline char is the first thing the getline function sees, that is all it gets.Pender
You need to use numeric_limits<streamsize>::max() to ignore all the characters, not 1000.Seafarer
According to the standard, .ignore() "Extract and discard characters," so I think that's pretty close to throwing away cplusplus.com/reference/istream/istream/ignoreBackache
Quibble: Do NOT use ignore begore getline. Sooner or later you'll find or create a case where the ignore is reached without the stream containing data you want ignored and wind up discarding data that you did want. Instead place ignore after operations you know leave unwanted data in the stream.Threat
P
46

You're thinking about this the wrong way. You're thinking in logical steps each time cin or getline is used. Ex. First ask for a number, then ask for a name. That is the wrong way to think about cin. So you run into a race condition because you assume the stream is clear each time you ask for a input.

If you write your program purely for input you'll find the problem:

int main()
{
    double num;
    string mystr;

    cin >> num;
    getline(cin, mystr);

    cout << "num=" << num << ",mystr=\'" << mystr << "\'" << endl;
}

In the above, you are thinking, "first get a number." So you type in 123 press enter, and your output will be num=123,mystr=''. Why is that? It's because in the stream you have 123\n and the 123 is parsed into the num variable while \n is still in the stream. Reading the doc for getline function by default it will look in the istream until a \n is encountered. In this example, since \n is in the stream, it looks like it "skipped" it but it worked properly.

For the above to work, you'll have to enter 123Hello World which will properly output num=123,mystr='Hello World'. That, or you put a cin.ignore between the cin and getline so that it'll break into logical steps that you expect.

This is why you need the ignore command. Because you are thinking of it in logical steps rather than in a stream form so you run into a race condition.

Take another code example that is commonly found in schools:

int main()
{
    int age;
    string firstName;
    string lastName;

    cout << "First name: ";
    cin >> firstName;

    cout << "Last name: ";
    cin >> lastName;

    cout << "Age: ";
    cin >> age;

    cout << "Hello " << firstName << " " << lastName << "! You are " << age << " years old!" << endl;
}

The above seems to be in logical steps. First ask for first name, last name, then age. So if you did John enter, then Doe enter, then 19 enter, the application works each logic step. If you think of it in "streams" you can simply enter John Doe 19 on the "First name:" question and it would work as well and appear to skip the remaining questions. For the above to work in logical steps, you would need to ignore the remaining stream for each logical break in questions.

Just remember to think of your program input as it is reading from a "stream" and not in logical steps. Each time you call cin it is being read from a stream. This creates a rather buggy application if the user enters the wrong input. For example, if you entered a character where a cin >> double is expected, the application will produce a seemingly bizarre output.

Pie answered 16/12, 2016 at 19:42 Comment(0)
H
26

Short answer

Why? Because there is still whitespace (carriage returns, tabs, spaces, newline) left in the input stream.

When? When you are using some function which does not on their own ignores the leading whitespaces. Cin by default ignores and removes the leading whitespace but getline does not ignore the leading whitespace on its own.

Now a detailed answer.

Everything you input in the console is read from the standard stream stdin. When you enter something, let's say 256 in your case and press enter, the contents of the stream become 256\n. Now cin picks up 256 and removes it from the stream and \n still remaining in the stream. Now next when you enter your name, let's say Raddicus, the new contents of the stream is \nRaddicus.

Now here comes the catch. When you try to read a line using getline, if not provided any delimiter as the third argument, getline by default reads till the newline character and removes the newline character from the stream. So on calling new line, getline reads and discards \n from the stream and resulting in an empty string read in mystr which appears like getline is skipped (but it's not) because there was already an newline in the stream, getline will not prompt for input as it has already read what it was supposed to read.

Now, how does cin.ignore help here?

According to the ignore documentation extract from cplusplus.com-

istream& ignore (streamsize n = 1, int delim = EOF);

Extracts characters from the input sequence and discards them, until either n characters have been extracted, or one compares equal to delim.

The function also stops extracting characters if the end-of-file is reached. If this is reached prematurely (before either extracting n characters or finding delim), the function sets the eofbit flag.

So, cin.ignore(256, '\n');, ignores first 256 characters or all the character untill it encounters delimeter (here \n in your case), whichever comes first (here \n is the first character, so it ignores until \n is encountered).

Just for your reference, If you don't exactly know how many characters to skip and your sole purpose is to clear the stream to prepare for reading a string using getline or cin you should use cin.ignore(numeric_limits<streamsize>::max(),'\n').

Quick explanation: It ignores the characters equal to maximum size of stream or until a '\n' is encountered, whichever case happens first.

Helaine answered 1/6, 2019 at 13:1 Comment(1)
is cin.get() also takes whitespace characters in account like cin.getline() ?Autotransformer
P
6

When you want to throw away a specific number of characters from the input stream manually.

A very common use case is using this to safely ignore newline characters since cin will sometimes leave newline characters that you will have to go over to get to the next line of input.

Long story short it gives you flexibility when handling stream input.

Possession answered 24/8, 2014 at 19:20 Comment(7)
Thanks for the response. Is there a more efficient way to use getline() after cin? Should I just plan on throwing an ignore line in between every line with a cin followed by a getline()? That seems pretty silly to me. Maybe it makes more sense when you have more experience with C++ than I do.Polysyllable
@Polysyllable It really is silly, especially since we know that '\n' is parsed as whitespace in IOStreams and IOStreams provide the std::ws manipulator which discards leading whitespace. All you have to do is std::getline(std::cin >> ws, mystr) to ignore all whitespace and newline characters.Tanh
@Raddicus: I would suggest you avoid mixing operator>> and getline on the same stream. For example, with cin, only use getline. If you need non-string data, then parse the string you got from getline using istringstream or stoi/stod/etc...J
instead of "sometimes", describe exactly when and whereTamelatameless
@0x499602D2 a good idea in this case but there will be other cases in which blank lines are significant (e.g. input with default values) so the issue still needs to be understoodTamelatameless
I think haphazardly down voting my answer seemed a bit harsh, I think the answer was appropriate given the open endedness of the question. Just an opinion.Possession
@Polysyllable I wasn't more specific in my answer because when and where to use ignore() and getline() and/or a combination of both will depend on the situation and your specific use cases and I think you will find this out as you work with C++ more in the future, I wish you luck!Possession
A
2

Ignore function is used to skip(discard/throw away) characters in the input stream. Ignore file is associated with the file istream. Consider the function below ex: cin.ignore(120,'/n'); the particular function skips the next 120 input character or to skip the characters until a newline character is read.

Allhallowtide answered 3/2, 2016 at 14:37 Comment(0)
S
0

As pointed right by many other users. It's because there may be whitespace or a newline character.

Consider the following code, it removes all the duplicate characters from a given string.

#include <bits/stdc++.h>
using namespace std;

int main() {
    int t;
    cin>>t;
    cin.ignore(); //Notice that this cin.ignore() is really crucial for any extra whitespace or newline character
    while(t--){
        vector<int> v(256,0);
        string s;
        getline(cin,s);
        string s2;
        for(int i=0;i<s.size();i++){
            if (v[s[i]]) continue;
            else{
                s2.push_back(s[i]);
                v[s[i]]++;
            }
        }
        cout<<s2<<endl;
    }
    return 0;
}

So, You get the point that it will ignore those unwanted inputs and will get the job done.

Sennar answered 16/2, 2020 at 11:47 Comment(0)
M
-3

It is better to use scanf(" %[^\n]",str) in c++ than cin.ignore() after cin>> statement.To do that first you have to include < cstdio > header.

Maraschino answered 25/12, 2016 at 7:38 Comment(1)
Not true. scanf is a part of the <cstdio> header, and is not the idiomatic C++ way of getting input from the user.Conchology

© 2022 - 2024 — McMap. All rights reserved.