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

Why do cin and getline exhibit different reading behavior?

提问人:Jeremy Fisher 提问时间:11/28/2021 最后编辑:einpoklumJeremy Fisher 更新时间:11/28/2021 访问量:782

问:

作为参考,我已经看过为什么 std::getline() 在格式化提取后跳过输入?

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

我的问题是 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 的实际实现是什么?

C++ 空格 IOSTREAM CIN GetLine

评论

2赞 Karl Knechtel 11/28/2021
“因此,在一个示例中,cin 似乎将光标推进到”\n“处,而在另一个示例中,它没有。不,无论哪种情况都没有。默认情况下,从 using 读取会跳过前导空格,而不是尾随空格。由于您的帐户已超过 8 年,并且您拥有多个金牌,因此您现在应该了解对研究的期望。像这样的问题很容易用搜索引擎回答cinoperator>>
0赞 Jeremy Fisher 11/28/2021
哦,有趣。但是,如果 cin 能够区分前导和尾随“\n”,那么为什么不跳过前导“\n”
0赞 Peter 11/28/2021
cin并且不会表现出不同的行为。格式化提取(使用运算符)都与流交互,您看到的是它们与(和任何流)的交互方式不同。他们这样做是因为它们的指定方式不同。 跳过空格,读取值(如果可以),并在到达空格时停止。 (默认情况下)继续读取,直到遇到换行符 - 并丢弃换行符。将它们一起使用可能会导致与某些用户输入的意外交互。getline()getline()<<cinoperator<<()getline()

答:

2赞 Sam Varshavchik 11/28/2021 #1

CIN 和 GetLine 的读取行为。

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

第一个 CIN 读取直到“\n”。一旦它击中“\n”,它就会递增 光标到下一个位置

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

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

您缺少的细节是在从输入流中提取值之前跳过空格(如果有),而不是之后>>

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

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

评论

0赞 Jeremy Fisher 11/28/2021
是的,但这是我的困惑。如果“>>”一直读到“\n”,但不读“\n”,则意味着“>>”的后续使用必须跳过前导“\n”。否则,光标将卡在“\n”处。但是 getline 似乎没有跳过 LEADING “\n”(换行符,而不是空格)。因此,getline 读取“\n”,但随后停止。因此,后续的 getline 操作开始从实际字符中读取。
0赞 Sam Varshavchik 11/28/2021
你的理解是准确的。 被认为是空格,我的回答中的所有内容都与您的评论一致。是什么让你期望跳过前导空格?当然不是。 跳过空格,不跳过。它们是不同的功能。他们做不同的事情。这就是它们存在的原因:它们做不同的事情。\nstd::getline>>getline
0赞 Eljay 11/28/2021
@JeremyFisher • 换行符空格。
0赞 Jeremy Fisher 11/28/2021
@Eljay那是我的困惑。我没有考虑“\n”空格。对我来说,“\n”只是一个特殊字符,代表“转到下一行”。“空格”对我来说意味着“ ”
1赞 Sam Varshavchik 11/28/2021
不,读取 ,它只是没有将其放入 . 读取 up 并包括 next ,并将它读取的所有内容(换行符除外)放入字符串中。getline\nstd::stringstd::getline\n
1赞 einpoklum 11/28/2021 #2

TL的;DR:这是因为 How 是面向内部的,而 GetLine 是面向线的。std::cin

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

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

    ...停在空白处或最大场宽处,以先到者为准。

    更一般地说,

    大多数转换 [例如,字符串的读取] 会丢弃初始空格字符

    (摘自 scanf() 手册页)

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

    阅读一整行......包含文本的缓冲区 ...包括换行符(如果已找到)。

    (摘自 getline() 手册页)

现在,C++的机制被格式化的输入匹配所取代,但具有类型安全性。(实际上,作为替代品是相当有问题的,但现在没关系了。作为 的替代品,它继承了它的许多功能,包括不喜欢拾取空白。std::cinscanf()std::cinstd::coutscanf()

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

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


PS - 您可以使用 std::cinskipws format-flag 来控制空格跳过行为

评论

0赞 Jeremy Fisher 11/28/2021
这也是一个很好的答案。一个问题 - 如果我有一个带有一堆换行符的字符串,例如“测试字符串\n\n\n\n\n\n\n\n\n\n\n 第二行”,2 个 getlines 背靠背会正确读取字符串吗?因为第一行应该在“第二行”之前将所有“\n”读成“”?
1赞 einpoklum 11/28/2021
@JeremyFisher:你需要的getline和你拥有的一样多,因为这些连续的指定空行的末尾。空行就是行。无论如何,请查阅手册页,或者对于 C++ 版本,请查阅 cppreference 页\n\n