几种 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}