微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

istream::ignore 是否丢弃超过 n 个字符?

如何解决istream::ignore 是否丢弃超过 n 个字符?

(这可能是 Why does std::basic_istream::ignore() extract more characters than specified? 的副本,但我的具体情况不涉及 delim)

来自 cppreference,istream::ignore 的描述如下:

从输入流中提取和丢弃字符,直到并包括 delim。

ignore 表现为 UnformattedInputFunction。在构造和检查哨兵对象后,它从流中提取字符并丢弃它们,直到出现以下任何一种情况:

  • 提取了count个字符。在 count 等于 std::numeric_limitsstd::streamsize::max() 的特殊情况下禁用此测试
  • 文件结束条件出现在输入序列中,在这种情况下,函数调用 setstate(eofbit)
  • 输入序列中的下一个可用字符 c 是 delim,由 Traits::eq_int_type(Traits::to_int_type(c),delim) 确定。分隔符被提取并丢弃。如果 delim 是 Traits::eof()
  • ,则禁用此测试

但是,假设我有以下程序:

#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;
  
}

现在,假设我在程序启动时输入了类似 p内容。我希望 cin '失败',然后 clear调用ignore 从缓冲区中丢弃 2 个字符。所以留在缓冲区中的 'p' 和 '\n' 应该被丢弃。但是,程序仍然期望在调用 ignore 之后输入,因此实际上只有在我给它超过 2 个字符以丢弃后才能到达最后的 std::cin>>p

我的问题: 输入类似 'b' 的内容并在第一次输入后立即按 Enter(所以在字符被丢弃后的 2 个,'p' 和 '\n')将 'b' 保留在缓冲区中并立即将其传递给 cin,而无需先打印信息。我如何才能在丢弃两个字符然后调用 << 后立即打印消息?

解决方法

经过多次来回评论(并自己重现问题),很明显问题在于:

  1. 您输入了无法解析的 p<Enter>
  2. 您尝试使用 ignore
  3. 恰好丢弃两个字符
  4. 您输出下划线
  5. 您提示输入下一个内容

但实际上事情似乎在第 2 步停止,直到您给它更多输入,而下划线只会在稍后出现。好吧,坏消息,你是对的,代码在 ignore 的第 2 步被阻塞。 ignore 正在阻止等待输入 第三个​​ 字符(实际上,检查这两个字符后面是否是 EOF),并且根据规范,这显然是正确的做法,我想吗?

这里的问题与 the problem you linked 的基本问题相同,只是表现形式不同。当 ignore 因为读取了请求的字符数而终止时,它总是尝试再读取一个字符,因为它需要知道条件 2 是否也可能为真(它碰巧读取了最后一个字符,以便它可以采取适当的操作,将 cin 置于 EOF 状态,或者将下一个字符留在缓冲区中以备下次读取):

效果:表现为未格式化的输入函数(如上所述)。构造哨兵对象后,提取字符并丢弃它们。字符将被提取,直到发生以下任一情况:

  • n != numeric_limits::max() (18.3.2) 到目前为止已经提取了 n 个字符
  • 文件结束出现在输入序列上(在这种情况下,函数调用 setstate(eofbit),这可能会抛出 ios_base::failure (27.5.5.4));
  • traits::eq_int_type(traits::to_int_type(c),delim) 用于下一个可用的输入字符 c(在这种情况下提取 c)。

由于您没有为 ignore 提供结束字符,它正在寻找 EOF,如果在两个字符后没有找到,则必须再读取一个以查看它是否在被忽略后出现字符(如果是,它将使 cin 处于 EOF 状态,如果不是,它偷看的字符将是您阅读的下一个字符)。

这里最简单的解决方案是不要尝试专门丢弃两个字符。你想通过换行符摆脱一切,所以这样做:

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

而不是std::cin.ignore(2);;它将读取任何和所有字符,直到换行符(或 EOF),消耗换行符,并且它永远不会过度读取(从某种意义上说,它会一直持续到找到分隔符或 EOF 为止,没有条件下它完成正在阅读大量字符,需要进一步查看)。

如果出于某种原因您想专门忽略两个字符(您怎么知道他们输入了 p<Enter> 而不是 pabc<Enter>?),只需调用 .get() 几次或.read(&two_byte_buffer,2) 之类的,因此您可以阅读原始字符而无需尝试peek 超越它们。

为了记录,这似乎与 cppreference 规范有点不同(可能是错误的);规范中的条件 2 没有指定它需要在读取计数字符后验证它是否处于 EOF,并且 cppreference 声称条件 3(需要查看)如果“分隔符”是默认值 {{1 }}。但是在您的其他答案中找到的规范引用不包括关于不申请 Traits::eof() 的条件 3 的那一行,并且条件 2 可能允许检查您是否处于 EOF,这将以观察到的行为结束。

,

您的问题与您的终端有关。当您按 ENTER 时,您很可能会得到两个字符——'\r''\n'。因此,输入流中还剩下一个字符可供读取。将该行更改为:

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

查看您期望的行为。

,

在缓冲区中传递确切数量的字符即可:

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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。