为什么 cin 和 getline 表现出不同的读取行为?

Why do cin and getline exhibit different reading behavior?

作为参考,我已经看过 Why does std::getline() skip input after a formatted extraction?

我想了解 cin 和 getline 的行为。我想象 cin 和 getline 是通过在输入缓冲区上循环来实现的,每次迭代都会增加一个游标。一旦输入缓冲区的当前元素等于某个“停止”值(cin 为“”或“\n”,getline 为“\n”),循环就会中断。

我的问题是cin和getline的读取行为的区别。对于 cin,它似乎在“\n”处停止,但它会在退出循环之前递增光标。例如,

string a, b;
cin >> a;
cin >> b;
cout << a << "-" << b << endl;
// Input: "cat\nhat"
// Output: "cat-hat"

所以在上面的代码中,第一个cin一直读到"\n"。一旦它碰到那个“\n”,它就会在中断循环之前将光标递增到下一个位置“h”。然后,下一个 cin 操作从“h”开始读取。这允许下一个 cin 实际处理字符而不是仅仅中断。

当 getline 与 cin 混合时,这不是行为。

string a, b;
cin >> a;
getline(cin, b);
cout << a << "-" << b << endl;

// Input: "cat\nhat"
// Output: "cat-"

在这个例子中,cin 读取到“\n”。但是当 getline 开始读取时,它似乎是从“\n”而不是“h”读取的。这意味着光标没有前进到“h”。所以 getline 处理了 "\n" 并将光标前进到 "h" 但实际上并没有将 getline 保存到 "b"。

所以在一个例子中,cin 似乎将光标前移到“\n”,而在另一个例子中,它没有。 getline 也表现出不同的行为。例如

string a, b;
getline(cin, a);
getline(cin, b);
cout << a << "-" << b << endl;

// Input: "cat\nhat"
// Output: "cat-hat"

现在getline实际上是将光标移到“\n”上。为什么会有不同的行为,当涉及到定界字符时,cin 与 getline 的实际实现是什么?

reading behavior of cin and getline.

cin 不“读取”任何内容。 cin 是一个输入流。 cin 正在从 读取 getline 从输入流中读取。格式化提取运算符 >> 从输入流中读取。读取的是 >>std::getlinestd::cin 没有自己的阅读。这是从.

读到的内容

first cin read up until the "\n". once it hit that "\n", it increments the cursor to the next position

不,不是。第一个 >> 运算符读取到 \n,但 没有读取它 \n 仍未读。

第二个 >> 运算符以换行符开始读取。 >> 运算符在提取预期值之前跳过输入流 中的所有空格

你缺少的细节是 >> 跳过空白(如果有的话)before 它提取来自输入流的值,而不是 after.

现在,>> 确实有可能在提取格式化值之前在输入流中找不到空格。如果 >> 的任务是提取一个 int,并且输入流刚刚打开并且它位于文件的开头,并且文件中的第一个字符是 1,那么, >> 根本不跳过任何空格。

最后,std::getline 不会跳过任何空格,它只是从输入流中读取直到读取 \n(或到达输入流的末尾)。

tl;dr: 因为 std::cin 是面向行内的,而 getline 是面向行的。

历史上,在 C 的标准库中,我们有函数 scanf()getline():

  • 当您告诉 scanf() 期待一个字符串时,它

    ... stops at white space or at the maximum field width, whichever occurs first.

    更一般地说,

    Most conversions [e.g. readings of strings] discard initial white space characters

    (来自scanf() man page

  • 当你调用getline()时,它:

    reads an entire line ... the buffer containing the text ... includes the newline character, if one was found.

    (来自getline() man page

现在,C++ 的 std::cin 机制取代了 scanf() 用于格式化输入匹配,但具有类型安全性。 (实际上 std::cinstd::cout 作为替代品是很有问题的,但现在不用管它了。)作为 scanf() 的替代品,它继承了它的许多特性,包括不喜欢拾取白色space.

因此,就像 scanf() 一样,运行 std::cin >> a 对于字符串 a 将在 \n 字符之前停止,并保留该换行符供将来使用的输入流。另外,就像 scanf() 一样,std::cin 的 >> 运算符会跳过前导白色 space,所以如果您第二次使用它,\n 将被跳过,并且从下一行的第一个非白色 space 字符开始拾取下一个字符串。

使用 std::getline(),您将获得与过去几十年完全相同的 getline() 行为。


PS - 您可以使用 std::cin[=38 的 skipws format-flag 来控制白space-跳过行为=]