几种 cin 输入方式的区别#

为了描述方便, 用语约定如下:

读入

从输入流中提取并使用内容, 输入流中将不再有该内容, 而该内容被实际使用.

读过

从输入流中提取并忽略内容, 输入流中将不再有该内容, 而该内容被忽略.

查看

查看输入流中内容, 输入流中内容不变.

输入流

通常输入区域就像管道里的水流一样, 你只能首先接触最开始的字符, 且没有回头路.

共同部分#

无论以下何种方式, 都会在到达文件尾 (eof, end of file) 时结束输入.

ch = cin.get()ch = cin.peek() 外, 所提及的方式均返回输入流 cin 本身, 从而允许链式调用和条件判断:

链式调用#
1std::cin >> value1 >> value2;
2getline(getline(std::cin, line), line);
条件判断: cin 能够隐式类型转换为 bool 类型, 相当于表达式 !cin.fail().#
1while (std::cin >> value) {
2  /* 读取成功 */
3}

输入存在缓冲区, 当我们通过键盘或其他方式进行输入时, 程序总是将内容先填入对应的缓冲区中. cin 即从它的缓冲区中读取要求的内容:

用键盘一次性输入若干内容#
1// 直接输入: 1 2 3
2for (int value = 0; std::cin >> value;) {
3  /* 第 1 次: value == 1 */
4  /* 第 2 次: value == 2 */
5  /* 第 3 次: value == 3 */
6}

提示

你也可以通过断点调试观察这个过程, 自己验证一下结果如何. 断点调试非常有用, 请学习 断点调试的使用 并完成习题.

格式化输入#

该方式输入的字符将会格式化为对应的类型值, 例如:

1int value;
2std::cin >> value;  // 读入的字符格式化为 int 类型值
cin >> value

默认情况下读过前面所有空白符, 然后读入字符直到查看到空白符.

非格式化输入#

该方式输入的就是字符本身.

cin.get(ch);ch = cin.get();

读入输入流中第一个字符.

ch = cin.peek();

查看输入流中第一个字符.

cin.get(char* output, streamsize count, char delim = '\n')

读入字符到 output 中, 直到读入了 count - 1 个字符或查看到分隔符 delim (默认为换行符 '\n'); 若确实有读入字符, 则在之后附上终止字符 :cpp:'\0'.

cin.getline(char* output, streamsize count, char delim = '\n')

读入字符到 output 中, 直到读入了 count - 1 个字符或读过了分隔符 delim; 若确实有读入字符, 则在之后附上终止字符 :cpp:'\0'.

提示

为什么是读入 count - 1 个字符? 因为这些输入方式认为输入内容将被作为一个字符串, 而字符串需要在尾部用 '\0' 表示终止, 故这些方式为 '\0' 预留一个位置.

getline(std::cin, std::string& output, char delim = '\n')

读入字符到 output 中, 直到读过了分隔符 delim. 相比于上面的, 这是 推荐的方式.

cin.ignore(streamsize count, char delim)

读过字符直到读过了 count 个字符或读过了分隔符 delim.

通常 #include <limits>, 然后以 std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n') 方式使用, 表示一直读过字符直到读过换行符 '\n'.

此外, 你也可以使用 std::cin >> std::ws, 这会读过所有的空白符 (空格、制表、换行等), 直到查看到非空白符.

cin.read(char* output, streamsize count)

读入 count 个字符到 output 中, 通常用于 二进制输入输出 (binary I/O).

放回输入流中#

cin.unget()

将最近读入/读过的字符放回 cin 中, 相当于 cin.get() 的逆向操作.

cin.putback(char ch)

ch 放回 cin 中, 之后通过 cin 进行输入时, 第一个字符将会是 ch.

从错误状态恢复过来#

数据读取失败后, cin 将会被设置成一个错误状态导致我们无法继续使用. 我们此时需要自己清理输入缓冲区中的残留数据 (毕竟已经出错了, 这些数据你已经不知道变成了什么样, 而且可能没用了), 并恢复 cin 的状态:

1#include <limits>
2using namespace std;
3
4cin.clear();  // 恢复状态
5cin.ignore(numeric_limits<streamsize>::max(), '\n');  // 清理缓冲区

当然有的状态没办法正常恢复 (一个极端的例子是突然停电了).

参见

输入失败的各种情况见于 文件读写.

最佳实践#

由于格式化输入和非格式化输入间的行为差异, 不建议混用格式化输入和非格式化输入.

如果确实需要一行行读取内容, 并在行内进行格式化输入, 可以参考以下代码:

 1#include <fstream>
 2#include <sstream>
 3#include <string>
 4
 5int main() {
 6  std::ifstream ifile("输入文件路径");
 7  std::string line;
 8  while (std::getline(ifile, line)) {  // ifile 输入流非格式化输入一行到 line 中
 9    std::istringstream iss(line);  // 将 line 中内容作为另一个输入流
10    int value = 0;
11    iss >> value;  // iss 输入流进行格式化输入
12  }
13}