Menu

C++ 输入与输出:cin、cout、getline 与流

C++ 中控制台输入输出的原理:用 cout 打印,用 cin 读取,cin 之后 getline 读到换行符的经典 bug,以及输入失败后如何恢复。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

C++ 如何与控制台交互

C++ 通过 <iostream> 头文件中的流来完成控制台输入输出。用 cout(字符输出)向外输出,用 cin(字符输入)读取用户的输入。它们用到了两个你之前在位移运算中见过的运算符,在这里被赋予了新的用途:

  • << 是插入运算符,它把数据送入 cout。
  • >> 是提取运算符,它从 cin 中取出数据,存入一个变量。

记住方向的一个简单办法:箭头指向的就是数据流动的方向。既然你已经能用字符串保存文本,接下来就让这些文本进出你的程序。

用 cout 打印

cout 把你插入的内容发送到标准输出。一条语句中可以连续使用多个 <<,自由地混合文本、数字和变量。

每个 << 都会追加到同一行,直到你插入一个换行。有两种方式可以换行:'\n' 插入一个换行字符,而 endl 插入换行符并且刷新缓冲区到屏幕。刷新是有实际开销的,所以在一个打印成千上万行的循环中,优先使用 '\n',流会在需要时自行刷新(程序退出时也一定会刷新)。

// Fine for a one-off message:
cout << "Done" << endl;

// In a hot loop, prefer this - no forced flush each iteration:
for (int i = 0; i < 1000000; ++i)
    cout << i << '\n';

用 cin 和 >> 读取

cin >> variable 读取一个以空白字符分隔的记号,并将其转换为变量的类型。它会跳过开头的空格或换行符,然后在下一个空白字符处停止。

由于 >> 遇到空白字符就停止,它很适合读取单个数字和单个单词,但读不了一整句话:输入 hello world 时,cin >> word 只会读到 hello,而 world 留在缓冲区中等待下一次读取。

用 getline 读取一整行

要读取一整行(包括所有空格),请使用 getline(cin, line),它会把按下 Enter 之前的全部内容读入一个 std::string。

只要输入中可能包含空格,比如姓名、地址、句子,它就是正确的工具。不过,一旦把 getline 和 >> 混用,就有一个陷阱在等着你。

getline 页面专门介绍了这个函数:自定义分隔符、一直读取到输入结束,以及用 std::ifstream 逐行读取文件。

cin 与 getline 的换行符陷阱

这是 C++ 输入输出中最常见的 bug。执行 cin >> n 时,提取操作读取了数字,却把换行符(你按下的 Enter 键)留在了输入缓冲区中。接下来的 getline 立刻看到这个残留的换行符,认为这一行已经结束,于是返回一个空字符串,根本没有停下来等待输入。

int age;
string city;

cin >> age;            // you type 30 and press Enter; '\n' stays in the buffer
getline(cin, city);    // reads the leftover '\n' -> city is "" (empty!)

解决方法是在 >> 之后、getline 之前,用 cin.ignore 丢弃这个残留的换行符:

cin.ignore(numeric_limits<streamsize>::max(), '\n') 会一直跳过字符,直到吃掉一个换行符(或到达输入末尾)。这是稳妥的写法:更短的 cin.ignore() 只丢弃一个字符,如果用户在数字后面多输入了空格就会出问题。养成使用完整写法的习惯。

输入失败时

如果用户在需要数字的地方输入了字母,提取操作就会失败:cin 进入错误状态,目标变量保持不变(从 C++11 起会被设为 0)。更糟的是,一旦 cin 处于失败状态,之后的每次读取也都会被静默跳过,你可能因此陷入死循环。

恢复总是分两步:cin.clear() 重置错误标志,让流重新可用;cin.ignore(...) 丢掉仍然卡在缓冲区里的错误字符。如果省略 ignore,错误的输入会一直留在那里,下一次 >> 又会失败,这就是经典的死循环。之所以能直接在条件中判断 cin >> n,是因为流处于失败状态时会转换为 false。

要避免的常见错误

  • 用 cin >> s 读取一句话。 它会在第一个空格处停止。凡是包含空格的内容都用 getline。
  • 在 >> 和 getline 之间忘了 cin.ignore。 残留的换行符会让你得到一个空行。先清空缓冲区。
  • 到处都用 endl。 每个 endl 都会强制刷新一次。默认使用 '\n',只在输出必须立即显示时才用 endl。
  • 忽视 cin 读取失败。 读取数字时输入了字母,cin 就会处于失败状态;再次读取之前,一定要先 clear() 再 ignore()。

常见问题

C++ 中为什么用了 cin 之后,getline 会直接跳过输入?

cin >> x 读取了数字,却把你按下的换行符留在了缓冲区里。接下来的 getline 读到这个残留的换行符,立即返回一个空字符串。在 >> 之后、getline 之前,先用 cin.ignore(numeric_limits<streamsize>::max(), '\n'); 清除它。

C++ 中 endl 和 \n 有什么区别?

两者都会结束当前行,但 endl 还会把输出缓冲区刷新到屏幕上,而 '\n' 只插入一个换行符。刷新是有代价的:在频繁执行的循环中,优先使用 '\n',让流自己决定何时刷新。只有在确实需要输出立刻显示时才使用 endl。

C++ 中如何读取包含空格的一整行文本?

使用 getline(cin, line),而不是 cin >> line。>> 运算符遇到第一个空白字符就停止,所以只能读到一个单词。getline 会把按下 Enter 之前的所有内容(包括空格)读入一个 std::string。

Coddy 编程语言插图

和 Coddy 一起学编程

开始