When and Why Do I Need to Use Cin.Ignore() in C++

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

Ignore is exactly what the name implies.

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

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 amount of chars before the specified break point, in this case, the \n newline character.)

What's the equivalent of cin.ignore() in C?

No, there is not. But let's look what happens behind the curtain called cin.ignore(). Let's take llvm libcxx sources, I find them faster to look through then gcc's.

The extern istream cin; is in iostream, but it is initialized on application startup in iostream.cpp using statically allocated buffer and __stdoutbuf object constructed from the good' old' FILE * stdin:

_ALIGNAS_TYPE (istream) char cin [sizeof(istream)];
ios_base::Init::Init() {
istream* cin_ptr = ::new(cin) istream(::new(__cin) __stdinbuf <char>(stdin) );
...

The istream::ignore() function can be found in istraem. It's pretty simple, first we check if the user wants to clean all chars from the stream or just some of them (if (__n == numeric_limits<streamsize>::max())). Then the function calls this->rdbuf()->sbumpc() in a loop predefined amount of counts (or endless, in case __n is equal to numeric_limits<steramsize::max()). We can find sbumpc() to be a member of std::basic_streambuf, from cppreference:

int_type sbumpc();
Reads one character and advances the input sequence by one character.

If the input sequence read position is not available, returns uflow(). Otherwise returns Traits::to_int_type(*gptr()).

So we can simply deduce that this->rdbuf() returns handle to __stdinbuf<char>(stdin). In the cin::ignore function the call to __stdinbuf<char>(stdin)::sbumpc() is made that many times, as many characters we want to ignore. So let's go to sbumpc()! First let's take a look at streambuf:

int_type sbumpc() {
if (__ninp_ == __einp_)
return uflow();
return traits_type::to_int_type(*__ninp_++);
}

So if (__ninp_ == __einp_) is doing some internal buffering in streambuf object, not to call uflow() if there are already buffered characters in our buffer. __ninp__ pointer get's incremented after each read, that must be it. uflow() is overloaded by __stdinbuf : public basic_streambuf< .... >, from __std_stream:

template <class _CharT>
typename __stdinbuf<_CharT>::int_type
__stdinbuf<_CharT>::uflow()
{
return __getchar(true);
}

Puff, let's go to __getchar and find out what the true parameter is. It's right below in __std_stream.

It's a long function, with the main functionality, which takes care of some buffering. But we can spot the hearth of this function right away:

template <class _CharT>
typename __stdinbuf<_CharT>::int_type
__stdinbuf<_CharT>::__getchar(bool __consume) {
....
int __c = getc(__file_);
if (__c == EOF)
return traits_type::eof();
...
}

Let's go from the beginning:

  • cin is an istraem object and is initialized from __stdinbuf<char>(stdin)
  • istream::ignore() calls basic_streambuf::sbumpc() predefined number of times, probably on an object initalized using stdin
  • basic_streambuf::sbumpc() takes care of some bufering and calls basic_streambuf::uflow() if the buffer is empty.
  • basic_streambuf::uflow() is overloaded by __stdinbuf::uflos() and calls __stdinbuf::__getchar()
  • __sinbuf::__getchar() calls getc(__file__) so probably getc(stdin) to read one character from the stream

To sumarize:

void stdin_ignore(size_t n, int delim)
{
while (n--) {
const int c = getc(stdin);
if (c == EOF)
break;
if (delim != EOF && delim == c) {
break;
}
}

Why is cin.ignore() necessary when using getline after cin, but is not required when using cin multiple times?

Because getline() reads until the next newline character from the given std::istream, while std::istream::operator>>() skips any whitespaces (Spaces, Tabs and newlines).

So when you read an integer or a floating point number, all trailing whitespaces are left in the input stream. When you read from a console or terminal, you type the data and hit Enter, the latter of which is left in the stream, and will be caught by getline() if you don't clear it.

You don't have to clear it because the next time you read a std::string, std::istream::operator>>() skips the whitespaces for you.


Consider this code segment;

std::string a, b;
std::cin >> a;
std::getline(std::cin, b);

and this input:

Stack Overflow<Enter>
Where developers learn.<Enter>

The first cin statement will read the word Stack, leaving a space and Overflow<Enter> behind. Then it'll be read by getline, so

assert(b == " Overflow");

If you insert a std::cin.ignore() before calling getline(), it will instead turn into

assert(b == "Where developers learn.");

Why do we need to use cin.ignore() before getline(cin, string)?

std::getline() only "skips" input if there is a leading newline in the stream which precedes the input you wish to read. This can come about if you previously performed a formatted extraction which left a residual newline. By default, std::getline() delimits extraction upon the acquisition of a newline character.

ignore() is a function which discards a certain amount of characters (by default the amount to discard is 1). If you use this preceding an unformatted extraction (like std::getline()) but following a formatted extraction (like std::istream::operator>>()) it will allow the data to be read as you expect because it will discard the residual newline.

I talk about this in detail in my answer here.

Why would we call cin.clear() and cin.ignore() after reading input?

The cin.clear() clears the error flag on cin (so that future I/O operations will work correctly), and then cin.ignore(10000, '\n') skips to the next newline (to ignore anything else on the same line as the non-number so that it does not cause another parse failure). It will only skip up to 10000 characters, so the code is assuming the user will not put in a very long, invalid line.

What cin.ignore() does exactly?

You don't need to use ignore every time, but it is good to use after formatted input, or in cases like yours where you only read a specified amount.

In your example, if I were to type in over 200 characters, any future input might be in for a rough surprise.

char input[200];
std::cin.get(input, 200);

After this executes, the first 200 characters were extracted, but anything after that is still left lying in the stream. It also leaves the newline ('\n') character in it. Anytime you want to extract input after this, it'll read in the remaining characters from our previous input.

This happens with formatted input, too. Take this example:

int age;
std::string name;
std::cin >> age;
std::getline(std::cin, name);

What you want is to type in an age, like 32, and a name, like "Bob". What happens is you input the age, and the program skips reading the name. When std::cin uses >> to read into variables, it leaves the '\n' character that was put into the stream by hitting enter. Then, getline reads in that newline character and stops, because it hit a newline and thinks it is done.

ignore solves this problem by discarding everything up to and including the next newline character, so that the extra input doesn't mess with future reads.

std::cin.ignore(std::numeric_limits<std::streamsize>::max());

How should the cin.ignore() function be used in this code?

You should Write cin.ignore(); before for loop . Your code ignoring first charecter of every string without first iteration. You need to ignore line break for only test not for every string .

see the below code :

int main() {

int test;
cin>>test;
cin.ignore();
for(int j = 0; j < test; j++){

char str[10000];
cin.getline(str,9999);
//cin>>str;

for( int i = 0; i < strlen(str); i++)
{
if(i % 2 == 0)
cout<<str[i];
}
cout <<" ";
for(int i = 0; i < strlen(str); i++)
{
if((i % 2 != 0))
cout<<str[i];
}

cout << endl;
}

return 0;
}

input :

5

Hacker

Rank

WoW

check

lastone



output :

Hce akr

Rn ak

WW o

cek hc

lsoe atn

why do I have to use cin.ignore when I not using getline()?

Basically the ignore() reads the rest of the line and drops it. You want to do this because otherwise you would simply try to re-read the same value again and again and always fail.

Example:

 Enter an integer:
Fify Six <-- If I typed that.

Then the line:

 cin >> i;                 // This will fail (as i is an int)
// and there is no integer value on the input.

So you fail to read the value. Note: this does not remove the input it is still waiting to be read. So if you simply clear the state and try again you will fail again as you are trying to read the same value repeatedly (always failing).

Now you could remove a single character and throw it away and try again. That would be a valid technique to try and eventually resolve this. And it would get there after 7 attempts (each attempt throwing away a character).

But usually you just throw away the whole line. This is because the std::cin is buffered and only passed to the application a line at a time. i.e. You type some text and then hit enter and the buffer is flushed to your code at that point for parsing. If the user had done something wrong, usually the whole line is wrong so discarding it just gets to the next set of user input.

You should note that this buffered input model is tied with the std::cout stream as well. It makes sure that when you attempt to read from the std::cin the output stream is flushed automatically so the question you are answering on the input matches what is being asked on the output. This leads to 1 question and 1 answer per line from the user. So incorrect input can simply be dropped a line at a time.

Now this does not stop the user from abusing the system and typing multiple answers onto one line. Which then are all ignored at the same time when an answer is wrong.



Related Topics



Leave a reply



Submit