几种 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
此外, 你也可以使用 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
2using namespace std;
3
4cin.clear(); // 恢复状态
5cin.ignore(numeric_limits
当然有的状态没办法正常恢复 (一个极端的例子是突然停电了).
参见
输入失败的各种情况见于 文件读写.
最佳实践#
由于格式化输入和非格式化输入间的行为差异, 不建议混用格式化输入和非格式化输入.
如果确实需要一行行读取内容, 并在行内进行格式化输入, 可以参考以下代码:
1#include
2#include
3#include
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}