【问题标题】:Why do cin and getline exhibit different reading behavior?为什么 cin 和 getline 表现出不同的阅读行为?
【发布时间】:2022-01-05 07:42:09
【问题描述】:

作为参考,我已经看过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 在分隔符方面的实际实现是什么?

【问题讨论】:

  • "所以在一个例子中,cin 似乎将光标推进到 "\n",而在另一个例子中,它没有。"不,在这两种情况下都不是。默认情况下,使用operator&gt;&gt;cin 读取会跳过前导 空格,而不是尾随空格。由于您的帐户已超过 8 年并且您有多个金徽章,您现在应该了解expectation for research。像这样的问题是easily answered 与搜索引擎。
  • 哦,有趣。但是如果cin能够区分前导和后缀“\n”,那么为什么getline不跳过前导“\n”
  • cingetline() 没有表现出不同的行为。 getline() 和格式化提取(使用运算符 &lt;&lt;)都与流交互,您所看到的是它们与 cin(和任何流)的交互方式不同。他们这样做是因为它们的指定方式不同。 operator&lt;&lt;() 跳过空白,读取值(如果可以),并在到达空白时停止。 getline()(默认情况下)一直读取,直到遇到换行符 - 并丢弃换行符。同时使用它们可能会导致与某些用户输入发生意外交互。

标签: c++ whitespace iostream cin getline


【解决方案1】:

cin 和 getline 的读取行为。

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

第一个 cin 读到 "\n"。一旦它击中“\n”,它就会增加 光标到下一个位置

不,它没有。第一个&gt;&gt; 运算符一直读取到\n,但不读取它\n 仍未阅读。

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

您缺少的细节是&gt;&gt; 跳过空格(如果有的话)它从输入流中提取值,而不是 之后

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

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

【讨论】:

  • 对,但这就是我的困惑。如果 ">>" 一直读到 "\n" 但没有读到 "\n",那么这意味着 ">>" 的后续使用必须跳过前导 "\n"。否则光标会停留在“\n”。但是 getline 似乎没有跳过 LEADING "\n" (换行符,而不是空格)。因此 getline 读取“\n”但随后停止。所以后续的 getline 操作从实际字符开始读取。
  • 你的理解是准确的。 \n 被视为空格,我的回答中的所有内容都与您的评论一致。是什么让您期望 std::getline 跳过前导空格?当然不是。 &gt;&gt; 会跳过空格,getline 不会。它们是不同的功能。他们做不同的事情。这就是它们存在的原因:它们做不同的事情。
  • @JeremyFisher • 换行空格。
  • @Eljay 那是我的困惑。我没有考虑“\n”空格。 “\n”对我来说只是一个代表“转到下一行”的特殊字符。 “空白”对我来说意味着“”
  • 不,getline 读取 \n,只是没有将其放入 std::stringstd::getline 最多读取并包括下一个\n,并将其读取的所有内容,除了换行到字符串中。
【解决方案2】:

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

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

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

    ...在空白处或最大字段宽度处停止,以先发生者为准。

    更一般地说,

    大多数转化 [例如字符串的读数] 丢弃初始的空白字符

    (来自scanf() man page

  • 当你拨打getline()时,它:

    读取整行...包含文本的缓冲区...包括换行符,如果找到的话。

    (来自getline() man page

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

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

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


PS - 您可以使用skipws format-flagstd::cin 控制空格跳过行为

【讨论】:

  • 这也是一个很好的答案。一个问题 - 如果我有一个带有一堆换行符的字符串,例如“测试字符串\n\n\n\n\n\n\n\n\n 第二行”,那么 2 个 getlines 是否会背靠背正确读取字符串?因为第一个 getline 应该在“第二行”之前读取所有“\n”到“”?
  • @JeremyFisher:您需要与\n 一样多的getline,因为那些连续的\n 指定空行的结尾。空行是一行。无论如何,请参阅手册页,或者对于 C++ 版本,请参阅 cppreference page
猜你喜欢
  • 2018-08-13
  • 1970-01-01
  • 2012-04-22
  • 2013-08-23
  • 1970-01-01
  • 2015-11-01
  • 2015-03-07
  • 1970-01-01
相关资源
最近更新 更多