【问题标题】:istream and cin.get()istream 和 cin.get()
【发布时间】:2012-08-28 18:17:58
【问题描述】:

我对这两段代码的区别有疑问:

char buffer5[5];
cin.get(buffer5, 5);
cout << buffer5;

cin.get(buffer5, 5);
cout << buffer5;

char buffer4;
while (cin.get(buffer4))
{
    cout << buffer4;
}

在第一段代码中,代码获取 5 个字符并将其放入 buffer5 中。然而,因为你按下了回车,在调用 get() 时不会将换行符放入流中,因此程序将终止并且不会再要求你输入另一轮 5 个字符。

在第二段代码中, cin.get() 等待输入流的输入,因此循环不只是终止(我认为)。假设我在输入流中输入了“Apple”。这会将 5 个字符放入输入流中,并且循环会将所有字符打印到输出中。但是,与第一段代码不同的是,它不会停止,即使在两次输入之后我也可以继续输入。

为什么我可以在第二段代码而不是第一段代码中连续输入字符序列到终端?

【问题讨论】:

  • get 返回一个istream &amp;,你永远不会导致循环条件为 false。尝试输入系统的 EOF 字符。
  • @chris:- 在我的评论下方还有一个框,称为 Your Answer....
  • @Anonymous,我个人认为答案可能比这多一点。
  • @chris,感谢您提供的信息
  • @chris,如果不是简单的答案,我今天的分数只有一半!说真的,如果这是对问题的真实答案,您应该将其发布。只有当它是推测时才会首选评论。

标签: c++ cin istream


【解决方案1】:

首先,除了在输入序列中输入换行符 (\n) 之外,“按 Enter”对 IOStream 没有特殊意义(注意,当使用文本流时,平台特定的行尾序列被转换为单个换行符)。在控制台上输入数据时,数据通常由控制台进行行缓冲,并且仅在按 Enter 时转发给程序(通常可以关闭此功能,但其详细信息是特定于平台的,无论如何与此问题无关)。

除此之外,让我们将注意力转移到s.get(buffer, n) 的行为上,该行为std::istream s 和一个指向至少n 字符buffer 的数组的指针。对此的描述非常简单:它调用s.get(buffer, n, s.widen('\n'))。由于我们正在讨论std::istream,而您可能还没有更改std::locale,我们可以假设s.widen('\n') 只返回'\n',即调用等效于s.get(buffer, n, '\n'),其中'\n' 称为分隔符问题就变成了这个函数的作用。

好吧,这个函数最多提取m = 0 &lt; n? n - 1: 0 个字符,当到达m 或下一个字符与流中留下的分隔符相同时停止(如果你使用std::istream::getline()) d 希望提取分隔符)。任何提取的字符都存储在buffer 的相应位置,如果0 &lt; n 一个空字符存储在buffer[n - 1] 位置。如果没有提取字符,则设置std::ios_base::failbit

好的,有了这个,我们应该知道谜语的所有成分:当您输入至少一个字符但少于 5 个字符时,第一次调用 get() 成功并将换行符作为缓冲区中的下一个字符。下一次尝试get() 更多字符立即找到分隔符,未存储任何字符,并通过设置std::ios_base::failbit 指示失败。这个理论很容易验证:

#include <iostream>

int main()
{
    char buffer[5];
    for (int count(0); std::cin; ++count) {
        if (std::cin.get(buffer, 5)) {
            std::cout << "get[" << count << "]='" << buffer << "'\n";
        }
        else {
            std::cout << "get[" << count << "] failed\n";
        }
    }
}

如果不输入任何字符,对std::cin.get() 的第一次调用将失败。如果输入 1 到 4 个字符,则第一次调用成功,第二次调用失败。如果输入超过 4 个字符,第二次调用也会成功,等等。有几种方法可以处理可能卡住的换行符:

  1. 只需使用std::istream::getline(),它的行为与std::istream::get() 相同,但如果这就是它停止读取的原因,也会提取分隔符。但是,这可能会将一行分成多个读取,这可能是也可能不是。
  2. 为避免固定行长度的限制,您可以将std::getline()std::string 一起使用(即std::getline(std::cin, string))。
  3. 在成功get() 之后,您可以在必要时使用std::istream::peek()std::istream::ignore() 检查下一个字符是否为换行符。

哪些方法可以满足您的需求,取决于您要达到的目标。

【讨论】:

  • 在您的代码中,else 条件在输入流中不再有任何内容时执行。但是,在我的问题的 while 循环中,假设输入流中确实有东西,程序会持续运行。克里斯在 cmets 中说 get() 总是返回 istream&,但与您的回答相矛盾。还有一个更好的方式来表达我的问题是为什么 while 循环永远不会终止,即使您输入各种字符串(按下 Enter 键时也会将换行符放入流中)?
  • 实际上,Chris 的回答与我的完全一致:get() 的两个版本都返回一个 std::istream,表示失败并转换为您可以测试的布尔值:true 没有错误标志设置和false 用于设置错误标志std::ios_base::failbitstd::ios_base::bad_bit。您的 while 循环只是提取单个字符并且只要有字符来就成功。重载 std::istream::get(char&amp;) 只有在没有更多字符要提取时才会失败(即设置 std::ios_base::failbit)。
  • 要让while 循环失败,您需要终止流。您可以执行此操作,例如,通过将文件重定向到您的程序(通常在命令行上使用 program &lt; file 之类的内容)或键入“文件结尾”字符(在 UNIX 上按 Ctrl-D,在 Windows 上按 Ctrl-Z,但也可能取决于系统;这不是由 C++ 标准标准化的)在一个合适的地方(通常必须紧跟在换行符之后)。
  • @AbhishekMane:请注意,格式化输入,例如,使用 std::cin &gt;&gt; ch,会跳过前导空格(除非您禁用这样做,例如,使用 std::cin &gt;&gt; std::noskipws)。相应地,ch 不会包含换行符(或任何其他空格)。此外,永远不要在不设置流大小的情况下使用格式化输入:std::cin &gt;&gt; std::setw(20) &gt;&gt; s;,否则您构建的 skmething 很容易溢出缓冲区。我认为您的主要困惑的答案是:格式化输入运算符通常在遇到不符合格式的字符时终止。
  • @AbhishekMane:很可能输入操作失败,ch 只包含它之前包含的任何内容[或者实际上未初始化但恰好包含格式化为空格的内容]:您是否验证过阅读ch 是否成功?在使用读取结果之前,您始终需要验证读取是否成功。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-09-15
  • 2013-04-20
  • 2015-01-08
  • 1970-01-01
  • 2012-11-01
  • 2014-03-10
  • 2016-03-10
相关资源
最近更新 更多