0x4*_*2D2 92 c++ iostream input c++-faq istream
我有以下代码提示用户输入他们的名字和状态:
#include <iostream>
#include <string>
int main()
{
std::string name;
std::string state;
if (std::cin >> name && std::getline(std::cin, state))
{
std::cout << "Your name is " << name << " and you live in " << state;
}
}
Run Code Online (Sandbox Code Playgroud)
我发现该名称已被成功提取,但不是州.这是输入和结果输出:
Run Code Online (Sandbox Code Playgroud)Input: "John" "New Hampshire" Output: "Your name is John and you live in "
为什么输出中省略了状态名称?我给出了正确的输入,但代码忽略了它.为什么会这样?
0x4*_*2D2 110
这与您自己提供的输入几乎没有关系,而是与默认行为std::getline()展示有关.当您提供名称(std::cin >> name)的输入时,您不仅提交了以下字符,而且还在流中附加了隐式换行符:
Run Code Online (Sandbox Code Playgroud)"John\n"
当您选择Enter或Return从终端提交时,新行始终会附加到您的输入中.它也用于文件中以移动到下一行.在提取之后,换行符将保留在缓冲区中,name直到下一个I/O操作被丢弃或消耗为止.当控制流到达时std::getline(),换行符将被丢弃,但输入将立即停止.发生这种情况的原因是因为此函数的默认功能指示它应该(它尝试读取一行并在找到换行符时停止).
因为这个领先的换行符会抑制程序的预期功能,所以必须以某种方式忽略它们.一种选择是std::cin.ignore()在第一次提取后调用.它将丢弃下一个可用字符,以便换行符不再具有侵入性.
这是std::getline()你所谓的超载:
Run Code Online (Sandbox Code Playgroud)std::getline(std::cin.ignore(), state)
此函数的另一个重载采用类型的分隔符charT.分隔符是表示输入序列之间边界的字符.input.widen('\n')默认情况下,此特定重载将分隔符设置为换行符,因为未提供一个分隔符.
现在,这些是std::getline()终止输入的一些条件:
std::basic_string<charT>可容纳的最大字符数第三个条件是我们正在处理的问题.您的输入state如此表示:
Run Code Online (Sandbox Code Playgroud)template<class charT> std::basic_istream<charT>& getline( std::basic_istream<charT>& input, std::basic_string<charT>& str )
哪个next_pointer是要解析的下一个字符.由于存储在输入序列中下一个位置的字符是分隔符,因此std::getline()将悄悄地丢弃该字符,next_pointer增加到下一个可用字符,并停止输入.这意味着您提供的其余字符仍保留在缓冲区中以进行下一个I/O操作.您会注意到,如果您从该行执行另一次读取state,您的提取将产生正确的结果作为最后一次调用以std::getline()丢弃该分隔符.
您可能已经注意到,在使用格式化输入运算符(operator>>())进行提取时,通常不会遇到此问题.这是因为输入流使用空格作为输入的分隔符,默认情况下设置std::skipws1个操纵器.当开始执行格式化输入时,Streams将丢弃流中的前导空格.2
与格式化输入运算符不同,std::getline()是未格式化的输入函数.所有未格式化的输入函数都有以下代码:
"John\nNew Hampshire"
^
|
next_pointer
Run Code Online (Sandbox Code Playgroud)
以上是一个sentry对象,它在标准C++实现中的所有格式化/未格式化的I/O函数中实例化.Sentry对象用于为I/O准备流并确定它是否处于失败状态.你只能在未格式化的输入函数中找到iftry构造函数的第二个参数true.该参数意味着不会从输入序列的开头丢弃前导空格.以下是标准[§27.7.2.1.3/ 2]的相关引用:
Run Code Online (Sandbox Code Playgroud)typename std::basic_istream<charT>::sentry ok(istream_object, true);[...]如果
noskipws为零且is.flags() & ios_base::skipws非零,则只要下一个可用输入字符c是空白字符,该函数就会提取并丢弃每个字符.[...]
由于上述条件为假,因此哨兵对象不会丢弃空格.这个函数noskipws设置的原因是true因为std::getline()要将原始的,未格式化的字符读入std::basic_string<charT>对象.
没有办法阻止这种行为std::getline().您需要做的是在std::getline()运行之前自己丢弃新行(但在格式化提取后执行此操作).这可以通过使用ignore()丢弃输入的其余部分来完成,直到我们达到一个新的新行:
explicit sentry(basic_istream<charT, traits>& is, bool noskipws = false);
Run Code Online (Sandbox Code Playgroud)
你需要包括<limits>使用std::numeric_limits.std::basic_istream<...>::ignore()是一个丢弃指定数量的字符的函数,直到找到分隔符或到达流的末尾(ignore()如果找到分隔符,也会丢弃该分隔符).该max()函数返回流可以接受的最大字符数.
丢弃空格的另一种方法是使用std::ws函数,该函数是一个操纵器,用于从输入流的开头提取和丢弃前导空格:
if (std::cin >> name &&
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n') &&
std::getline(std::cin, state))
{ ... }
Run Code Online (Sandbox Code Playgroud)
有什么不同?
区别在于ignore(std::streamsize count = 1, int_type delim = Traits::eof())3不加选择地丢弃字符,直到它丢弃count字符,找到分隔符(由第二个参数指定delim)或命中流的末尾.std::ws仅用于从流的开头丢弃空白字符.
如果要将格式化输入与未格式化输入混合,并且需要丢弃残留空格,请使用std::ws.否则,如果您需要清除无效输入而不管它是什么,请使用ignore().在我们的示例中,我们只需要清除空格,因为流消耗"John"了name变量的输入.剩下的就是换行符.
1:std::skipws是操纵器,它告诉输入流在执行格式化输入时丢弃前导空格.可以使用std::noskipws操纵器关闭此功能.
2:输入流默认将某些字符视为空格,如空格字符,换行符,换页符,回车符等.
3:这是签名std::basic_istream<...>::ignore().您可以使用零参数调用它来丢弃流中的单个字符,使用一个参数来丢弃一定数量的字符,或者使用两个参数来丢弃count字符或直到它到达为止delim,无论哪个先出现.如果你不知道分隔符前面有多少个字符,你通常会使用它std::numeric_limits<std::streamsize>::max()作为值count,但是你想要丢弃它们.
小智 10
如果您通过以下方式更改初始代码,一切都会正常:
if ((cin >> name).get() && std::getline(cin, state))
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
15492 次 |
| 最近记录: |