istream and cin.get()

istream and cin.get()

本文关键字:get cin and istream      更新时间:2023-10-16

我有一个关于这两段代码之间区别的问题:

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中。但是,因为您按enter键,所以在调用get()时不会在流中放入换行符,因此程序将终止,并且不会要求您再输入一轮5个字符。

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

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

首先,除了在输入序列中输入换行符(n)之外,"按enter"对IOStreams没有特殊意义(注意,当使用文本流时,特定于平台的行尾序列会转换为单个换行符)。在控制台上输入数据时,数据通常由控制台进行行缓冲,只有在按下回车键时才会转发到程序(通常可以关闭此功能,但其细节是特定于平台的,与此问题无关)。

有了这一点,让我们将注意力转向std::istream s和指向至少包含n个字符buffer的数组的指针的s.get(buffer, n)的行为。对其作用的描述非常琐碎:它调用s.get(buffer, n, s.widen('n'))。由于我们讨论的是std::istream,而您可能没有更改std::locale,我们可以假设s.widen('n')只返回'n',即该调用等效于s.get(buffer, n, 'n'),其中'n'被称为分隔符,问题变成了该函数的作用。

这个函数最多提取m = 0 < n? n - 1: 0个字符,当达到m或下一个字符与流中的分隔符相同时停止(如果希望提取分隔符,则使用std::istream::getline())。任何提取的字符被存储在buffer的相应位置中,并且如果0 < 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 << "] failedn";
        }
    }
}

如果未输入任何字符,则对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()来检查下一个字符是否为换行符

这些方法中的哪一种能满足你的需求取决于你想要实现的目标。