简体   繁体   中英

Does istream::ignore discard more than n characters?

(this is possibly a duplicate of Why does std::basic_istream::ignore() extract more characters than specified? , however my specific case doesn't deal with the delim)

From cppreference, the description of istream::ignore is the following:

Extracts and discards characters from the input stream until and including delim.

ignore behaves as an UnformattedInputFunction. After constructing and checking the sentry object, it extracts characters from the stream and discards them until any one of the following conditions occurs:

  • count characters were extracted. This test is disabled in the special case when count equals std::numeric_limitsstd::streamsize::max()
  • end of file conditions occurs in the input sequence, in which case the function calls setstate(eofbit)
  • the next available character c in the input sequence is delim, as determined by Traits::eq_int_type(Traits::to_int_type(c), delim). The delimiter character is extracted and discarded. This test is disabled if delim is Traits::eof()

However, let's say I've got the following program:

#include <iostream>
int main(void) {
  int x;
  char p;
  if (std::cin >> x) {
    std::cout << x;
  } else {
    std::cin.clear();
    std::cin.ignore(2);
    std::cout <<   "________________";
    std::cin >> p;
    std::cout << p;
  
}

Now, let's say I input something like p when my program starts. I expect cin to 'fail', then clear to be called and ignore to discard 2 characters from the buffer. So 'p' and '\n' that are left in the buffer should be discarded. However, the program still expects input after ignore gets called, so in reality it's only get to the final std::cin>>p after I've given it more than 2 characters to discard.

My issue: Inputting something like 'b' and hitting Enter immediately after the first input (so 2 after the characters get discarded, 'p' and '\n') keeps 'b' in the buffer and immediately passes it to cin, without first printing the message. How can I make it so that the message gets printed immediately after the two characters are discarded and then << is called?

After a lot of back and forth in the comments (and reproducing the problem myself), it's clear the problem is that:

  1. You enter p<Enter> , which isn't parsable
  2. You try to discard exactly two characters with ignore
  3. You output the underscores
  4. You prompt for the next input

but in fact things seem to stop at step 2 until you give it more input, and the underscores only appear later. Well, bad news, you're right, the code is blocking at step 2 in ignore . ignore is blocking waiting for a third character to be entered (really, checking if it's EOF after those two characters), and by the spec, this is apparently the correct thing to do, I think?

The problem here is the same basic issue as the problem you linked just a different manifestation. When ignore terminates because it's read the number of characters requested, it always attempts to reads one more character, because it needs to know if condition 2 might also be true (it happened to read the last character so it can take the appropriate action, putting cin in EOF state, or leaving the next character in the buffer for the next read otherwise):

Effects: Behaves as an unformatted input function (as described above). After constructing a sentry object, extracts characters and discards them. Characters are extracted until any of the following occurs:

  • n:= numeric_limits:.max() (18.3.2) and n characters have been extracted so far
  • end-of-file occurs on the input sequence (in which case the function calls setstate(eofbit), which may throw ios_base::failure (27.5.5.4));
  • traits::eq_int_type(traits::to_int_type(c), delim) for the next available input character c (in which case c is extracted).

Since you didn't provide an end character for ignore , it's looking for EOF, and if it doesn't find it after two characters, it must read one more to see if it shows up after the ignored characters (if it does, it'll leave cin in EOF state, if not, the character it peeked at will be the next one you read).

Simplest solution here is to not try to specifically discard exactly two characters. You want to get rid of everything through the newline, so do that with:

std::cin.ignore(std::numeric_limits<std::stringsize>::max(), '\n');

instead of std::cin.ignore(2); ; that will read any and all characters until the newline (or EOF), consume the newline, and it won't ever overread (in the sense that it continues forever until the delimiter or EOF is found, there is no condition under which it finishes reading a count of characters and needs to peek further).

If for some reason you want to specifically ignore exactly two characters (how do you know they entered p<Enter> and not pabc<Enter> ?), just call .get() on it a couple times or .read(&two_byte_buffer, 2) or the like, so you read the raw characters without the possibility of trying to peek beyond them.

For the record, this seems a little from the cppreference spec (which may be wrong); condition 2 in the spec doesn't specify it needs to verify if it is at EOF after reading count characters, and cppreference claims condition 3 (which would need to peek) is explicitly not checked if the "delimiter" is the default Traits::eof() . But the spec quote found in your other answer doesn't include that line about condition 3 not applying for Traits::eof() , and condition 2 might allow for checking if you're at EOF, which would end up with the observed behavior.

Your problem is related to your terminal. When you press ENTER, you are most likely getting two characters -- '\r' and '\n' . Consequently, there is still one character left in the input stream to read from. Change that line to:

std::cin.ignore(10, '\n'); // 10 is not magical. You may use any number > 2

to see the behavior you are expecting.

Passing exact number of characters in buffer will do the trick:

std::cin.ignore(std::cin.rdbuf()->in_avail());

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM