【问题标题】:Does istream::ignore discard more than n characters?istream::ignore 是否丢弃超过 n 个字符?
【发布时间】:2021-01-08 18:48:46
【问题描述】:

(这可能与Why does std::basic_istream::ignore() extract more characters than specified? 重复,但我的具体情况不涉及分隔符)

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

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

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

  • 个字符已提取。在 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 '失败',然后调用clearignore 从缓冲区中丢弃2 个字符。所以留在缓冲区中的'p'和'\n'应该被丢弃。但是,在调用 ignore 之后,程序仍然需要输入,所以实际上它只有在我给它超过 2 个字符以丢弃后才能到达最终的 std::cin&gt;&gt;p

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

【问题讨论】:

  • 总输入为 p\nb\n?
  • 我忘了它,但事实证明 '\r' 也在那里,因为它是 WSL。
  • 您能更清楚地了解您期望发生的事情吗?无论行尾样式如何,代码似乎都能正常运行,因为charb)的格式化输入将绕过ignore无论如何都错过的前导空格,leading to an output of ________________b无论行尾数据和代码翻译正在发挥作用。你期待什么?
  • 我希望我的程序在 'p' 被给出并丢弃(以及伴随的 '\n' 或 '\r')之后等待输入(对于 'b'),但是现在,p(变量)从缓冲区中获得一个值,该值来自打印消息之前的值。 @ShadowRanger
  • @pol:因此,当您“立即”键入 p&lt;Enter&gt;b&lt;Enter&gt;(我们将忽略回车的可能性)时,您基本上希望 b\n(以及缓冲区中当前可用的任何其他输入)被扔掉,只处理的东西?也就是说,如果输入了p\n,并且在b\n之前有一个延迟,你想读取b,但是如果b从一开始就在那里,你想丢弃它并等待为了别的?

标签: c++ io istream


【解决方案1】:

在 cmets 中反复多次后(并自己重现问题),很明显问题在于:

  1. 您输入p&lt;Enter&gt;,这是不可解析的
  2. 您尝试使用ignore 恰好丢弃两个字符
  3. 你输出下划线
  4. 提示输入下一个输入

但实际上事情似乎在第 2 步停止,直到您提供更多输入,并且下划线仅在稍后出现。好吧,坏消息,你是对的,代码在ignore 的第 2 步被阻塞。 ignore 正在阻止等待输入 third 字符(真的,检查这两个字符之后是否是 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&lt;Enter&gt; 而不是pabc&lt;Enter&gt;?),只需调用.get() 几次或.read(&amp;two_byte_buffer, 2) 或类似,因此您可以阅读原始字符,而无需尝试peek

作为记录,这似乎有点来自 cppreference 规范(这可能是错误的);规范中的条件 2 未指定它需要在读取计数字符后验证它是否处于 EOF,并且如果“分隔符”是默认值 Traits::eof(),则 cppreference 声明条件 3(需要查看)明确不检查.但是在您的其他答案中找到的规范报价不包括关于条件 3 不适用 Traits::eof() 的那一行,并且条件 2 可能允许检查您是否在 EOF,这最终会与观察到的行为。

【讨论】:

  • 非常感谢,但在你的倒数第三个例子中:当我这样做时,按下p&lt;enter&gt; 下划线不会显示,直到我至少再给出一个字符,剩下的在缓冲区中。这就是我不明白的,这是不正常的行为吗?
  • @pol:如果将cout 行更改为std::cout &lt;&lt; "________________" &lt;&lt; std::flush;,行为是否会改变?你可能有一个std::cin.tie(NULL); 某处未显示that's disabling the autoflush of std::cout when std::cin is polled
  • 不,同样的事情@ShadowRanger
  • @pol:好吧,问题和我想的相反,但至少我现在明白了。看着。
  • @pol:答案已更新。解决方案是不要将ignore 用于固定计数,将其用于无限计数,直到仅指定分隔符,或者以不同的方式读取。
【解决方案2】:

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

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

查看您期望的行为。

【讨论】:

  • 我认为你是对的(使用 WSL),但我不完全明白为什么会这样。无论如何,'b'之后的'\r\n'不应该仍然意味着,在'\r'之后,它应该停止从输入中读取吗?
  • 那么为什么它在丢弃'\r'后不立即打印消息呢?对不起,如果很明显,我仍然对此感到困惑。
  • @pol,我会晚一点回复。遇到紧急事情。
  • @pol:我正要纠正这个问题,指出cin on Windows is opened in text mode 所以这不应该发生(cin 将无缝地将\r\n 序列视为单个字符\n),但是如果您使用的是 WSL,那么您就不是在正常意义上的 Windows(它使用 Linux 规则,不进行行尾转换,因为 Linux 行仅以 \n 结尾),但您可以轻松处理由 Windows 工具生成的文件将使用 \r\n 行结尾。
  • @ShadowRanger 不管最后输入格式如何,cin.ignore(2) 我仍然不明白为什么程序期待输入,即使它读取 'p\n' 或 'p\r \n' 它不应该丢弃前两个并立即打印消息吗?但这不是这里发生的事情
【解决方案3】:

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

std::cin.ignore(std::cin.rdbuf()-&gt;in_avail());

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-15
    • 2015-02-16
    • 2014-11-26
    • 1970-01-01
    • 2019-06-26
    • 1970-01-01
    相关资源
    最近更新 更多