高程整理2-C++输入输出
2.C++输入输出
基本
- cin、cout、<<、>>
cin、cout都是对象。<<、>>此处是流插入、提取运算符,原来是位操作运算符,7级优先级,经过重载变成现在的功能。重载运算符现在不学。- cout << a;实际上是表达式语句。可以使用对表达式分析的方式进行解释。
<<在cout对象和a变量之间进行运算。运算的值是cout对象本身。cout << a,b,c;实际上是一个逗号表达式,表达式的值是c,最终只会输出a的值。
- 缓冲区
- cout被插入数据后,存储在缓冲区内,当满足条件(缓冲区满/endl/‘\n’/使用
cout.flush();刷新)才会集中输出一次。Windows系统下对这个规则的遵守不如Linux规范。 - cin不会被直接提取数据,输入的数据会存储在缓冲区内,当满足条件(缓冲区满/‘\n’)才会集中被提取一轮数据。
- cout被插入数据后,存储在缓冲区内,当满足条件(缓冲区满/endl/‘\n’/使用
- 输入输出设备均可以更改,称为输入/输出重定向。
- 输出会自动判断数据类型,选择合适的方法输出。char类型会自动按照字符输出。如果想要按数字或者按字符输出,强制类型转换即可。
- 奇技淫巧:不用强制类型转换,但是让char以整数的方式输出:
cout << ch+0 << endl;运算那里的涉及整型提升。 - cin 不能把数据提取到常量、表达式里去
cin/cout格式控制符
- 考试会给出
| 控制符 | 作用 |
|---|---|
| dec | 设置整数为 10 进制 |
| hex | 设置整数为 16 进制 |
| oct | 设置整数为 8 进制 |
| setbase(n) | 设置整数为 n 进制 (n=8,10,16) |
| setfill© | 设置填充字符,c 可以是字符常量或字符变量 |
| setprecision(n) | 设置实数的精度为 n 位。在以一般十进制形式输出时,n 代表有效数字。在以 fixed (固定小数位) 形式和 scientific (指数) 形式输出时,n 为小数位数 |
| setw(n) | 设置字段宽度为 n |
| setiosflags(ios::fixed) | 设置浮点数以固定的小数位数显示 |
| setiosflags(ios::scientific) | 设置浮点数以科学计数法(即指数形式)显示 |
| setiosflags(ios::left) | 输出数据左对齐 |
| setiosflags(ios::right) | 输出数据右对齐 |
| setiosflags(ios::skipws) | 忽略前导的空格 |
| setiosflags(ios::uppercase) | 在以科学计数法输出 E 和十六进制输出字母 X 时,以大写表示 |
| setiosflags(ios::showpos) | 输出正数时,给出 “+” 号 |
| resetiosflags(*) | 终止已设置的输出格式状态,括号内为具体内容 (本处用 * 替代) |
cin
cin的输入终止条件是回车、空格、非法输入。cin的读值遵循“合理且最长原则”,或者可以描述为“贪心”。先把可以读的读到,然后才去看对不对。这个原则和之前字符常量8进制转义的读取逻辑是很像的。详细规则见下方cin读入规则。- 读数值的时候,如果直接上非法字符或数值超过目标变量的上下限,那么实际的读入量将会不可信,此时讨论这个读入的东西没有意义,不同编译器都不一样。此时
cin.good()为0. - 要判断读入的值可不可信,可以使用
cin.good()或者cin.fail(),按照字面意思,大部分情况下是互斥的。会有少部分情况出现矛盾。(查阅得知这个东西是用于检查当前输入流是否正常,所以能拿来检查输入正不正常。) - 一旦
cin.good()变成0,后面所有的cin读入都不会成功执行。 - 无符号错误规律和有符号不一样,有符号的超出范围就会good变0,而无符号整数输入绝对值超范围才会good变0.负数但绝对值没抄是当作正常操作的。有符号的整数这里和赋值超范围的规则是不一样的。有符号赋值超范围只是会警告截断。但这里直接让输入流异常。
cin永远无法读取空格、换行,因为把这些当作分隔符。cin读取浮点数的时候,输入".3"可以用于代表"0.3"。
cin读取字符的时候,只能读图形字符,不能输入换行,不能输入空格。转义符方式无法输入控制字符。‘ " \ 都当单字符。cin不能跟endl。cin读东西,如果缓冲区没读完,就继续读缓冲区里的东西。- ctrl+c表示程序结束,这也会导致
cin.good()变成0,之后发生的事都不可信 - ctrl+z表示输入结束,这也会导致
cin.good()变成0,之后cin后变量的数据都不可信 cin进bool,可以输0和1,不能输truefalse,不能输除了0和1以外的任何数字,否则都会cin.good()变0.cin连续输入多个int时,碰到非法字符,下一个是0,再下面才是随机值
cin >>str;不能读有空格的字符串,输入多个的时候,空格是分隔符,与scanf不一样的是,超出长度不会报错,会截取能保证数组尾部有尾0的部分,比scanf安全,最后没有换行符。
cin读入规则
- cin读数据的流程:
- 按照合理且最长原则的某一条规则读取一次数据。
- 读取到非法的东西,或者到空格和换行了,结束本次读取,开始将读好的东西转成目标形式:
- 能够成功转换(类型不同,只要是读入的东西占用内存小于等于目标类型,都是可以成功的):成功完成本次输入。
- 转换失败(没有读到东西,读入的东西占用内存小于等于目标类型):good()置0.
- 大的数据读进小的空间,会出现越界写的危险行为,所以不可以。
| 第一个字符 | 后续字符 | |
|---|---|---|
| 整型 | +、-、数码 | 数码 |
| 浮点型 | +、-、数码、小数点 | 数码、第一次出现的小数点、第一次出现的e |
| 字符 | 一个非空格非回车的字符 | / |
| 字符串 | 待后续补充 | 待后续补充 |
此外的都是非法情况。如果读到了非法的字符,数据是不会从缓冲区拿出来的,非法字符还在那里面。
提示:浮点的情况,读到.3,得值0.3
- 读到无符号整数的时候,由于使用的规则不区分有没有符号,所以负数是合法的,然后如果和目标类型又是兼容的,那可以读进来,并发生自然而然的类型转换(二进制没变)。这时good()是1.对cin来说这属于可信范畴。
cin读一些整数
- signed整数,以short(-32768~32767)为例:
- 输入在[-32768,32767]内的数字,都正常
- 输入小于-32768大于32767的,会置错误标记。
- unsigned整数,以unsigned short (0~65535)为例:
- 输入在[-65536,65535]内的数字,都不会触发错误标记,其中的负数,是低位截断了
- 输入小于-65536大于65535的,会置错误标记。
cin格式化
- hex/oct/dec/setbase(n)可按照对应的进制输入数据。
- setiosflags(ios::skipws)忽略前导空格
- "忽略前导空格"的意思,把数据间的空格忽略,作为分割符
- 这个东西默认有效的,即不设置也生效,是不需要手动setiosflags的
- 想取消"忽略前导空格"的设置,把空格作为输入数据,应使用
cin.unsetf(ios::skipws);此时空格不作为分隔符,而是做为输入数据(如果输入整数遇到空格会当作非法字符,直接置异常标记)
cin部分成员函数
- cin.get()
功能:从输入流中读一个字符并返回该字符,可获取’ ‘和’\n’ - cin.get(字符变量)
功能:同上,从输入流中读一个字符给字符变量,返回cin(流对象自身) - cin.get(字符数组,字符个数n,中止字符)
功能:从输入流中读n-1个字符,若遇到中止字符,则提前结束,返回cin(流对象自身)
不写中止字符也不会输进来’\n’
某种程度上这个n代表最后这个字符串被写入n个字符,包含了最后的尾零。
- cin.getline(字符数组,字符个数n,中止字符)
功能:同三个参数的cin.get() - cin.eof()
功能:判断是否遇到了文件结束符EOF,返回逻辑值 (遇到EOF为真)
EOF(End Of File)是宏定义 - cin.peek()
功能:返回输入流中的下一个字符(不提取)(遇见文件结束符则返回EOF) - cin.putback(字符变量/字符常量)
功能:将字符变量/常量插入到输入流的头部 - cin.ignore(字符个数n,中止字符)
功能:跳过n个字符,或遇到中止字符时提前结束。这个中止字符也会被跳过。
cout
- 使用格式化输出要用iomanip头文件
setiosflags(ios::left/right)调整左右对齐。setiosflags(ios::fixed/scientific)调整输出是小数还是科学记数法。这两个在转变前一定要resetiosflags(xx)一下,否则会坏掉。- 为什么要求C++里必须用cout而不能用printf:用printf那个OI对速度的追求。但在实际的工程里面,printf没有安全检查,cout在后面可以重载。在工程的应用里printf没法和cout打。
- 实际上,输出这个行为非常非常的慢,程序运行的大部分时间都花在输入输出,普通的这种纯计算对CPU来说是很快的。
- cout输出字符数组的时候,如果没有尾零会继续越界
cout格式化
- 想要让 cout 的输出按照不同的进制,可以使用前导进制转换
cout << [jinzhi] << ...,其中,hex是16,dec是10,oct是8.这个不需要iomanip。这种输出只会输出最基本的数字表示,不会带 0x 之类的标记。 - 2 进制没有直接的输出方法。
- hex/oct/dec在使用以后,是对之后输出的所有整数生效,对浮点数不生效。
setbase(n)- 允许的合法值有8,10,16
- 当出现非法值时,处理方法是忽略输入值,以默认的10进制输出
- 对后面的所有整数有效,直到用另一个setbase去改变为止
setiosflags(ios::uppercase)- 用来在16进制形式的输出中,让a~f的字母改用大写的形式,不会改变其他字符输出的小写字母
- 对后面的所有数据有效
- 恢复小写,具体的做法是输出前进行格式控制:
resetiosflags(ios::uppercase)进行重置
setiosflags(ios::showpos)- 用于设置数字显示符号。无论这个东西设不设置,负数都会显示负号,所以相当于就是在调整正数显不显示正号。
- 对后面的所有整数(无论进制)和浮点数都生效
- 取消,具体的做法是输出前用
resetiosflags(ios::showpos)重置
setprecision(n)- 设置浮点数输出的有效位数/小数点后位数
- 对后面的所有浮点数生效
- 单独使用,指定的输出位数是有效位数
- 如果显示精度到达得到规定精度,就正常输出10进制整数(进制标记不影响他),如果精度不能到达个位,则按照科学记数法(
1.23e+05这种)输出。 - 如果在保留有效位数的过程中有效位数以内的末尾出现了0,则都忽略掉,不输出0.
setiosflags(ios::fixed)- 对后面的所有浮点数生效
- ios::fixed,指定的输出位数是小数点后位数
- 不采用科学记数法,直接输出小数,设置保留多少位小数则输出多少位小数,尾0不忽略也输出。
setiosflags(ios::scientific)- 对后面的所有浮点数生效
- 指定的输出位数是尾数的小数点后位数
- 使用科学计数法输出,保留的是尾数的小数点后位数,且不忽略尾0
- 上面两个标志原则上是互斥的,标准规定,同时使用
ios::scientific和ios::fixed会被解释为十六进制浮点格式(0x1.abcdefp+04这种) setw(n)setw()的意义就是,输出后面这个东西,无论长度是什么,一定要占满多少位,不够用填充字符填充,默认空格- setw指定的宽度,是总宽度(对于实型数据,包含小数点),当总宽度n大于数据宽度时,显示规律为在数据前面/后面补足用于填充的字符到总长达到设定总宽度为止;当总宽度n小于数据宽度时,显示规律为按照原样数据宽度输出
- 对后面的仅一个数据有效
setfill(c)- 设置用于补足填充的字符
- 对后面的所有数据有效
setiosflags(ios::left)- 设置数据左对齐,在右边补齐填充符号
- 不设置,默认是右对齐
- 同时使用
ios::left/ios::right,右对齐优先级高 boolalpha可以让输出布尔值的时候不是输出0/1,而是true/false
cout部分成员函数
- cout.put(字符常量/字符变量)
功能:向标准输出设备输出一个字符 - cout.write(字符串常量/变量,输出长度)
功能:向标准输出设备输出n个字符(如果n超过串长,则输出串长) - 下面这些和之前cout格式化那里的一样的
- cout.setf(控制标记)
功能:设置指定的控制标记(常用控制标记就是之前那些ios::下的东西)(setiosflags()) - cout.unsetf(控制标记)
功能:清除指定的控制标记(resetiosflags()) - cout.width(宽度)
功能:设置指定的输出宽度(setw(n)) - cout.fill(字符常量/字符变量)
功能:设置填充字节(setfill(c)) - cout.precision(精度)
功能:设置浮点数的输出精度(setprecision(n))
输入错误处理
- 这里面包含了c的输入输出。但是不可能拆开,就放在C++这里了
基本原则
基本范式:
1 | while(1){ |
一些原则:
多选择少输入:用户的行为都是不可控的,尽量让用户选择你给定的值,而不要让用户输入。
多个输入值,一个值cin一次,cin.fail()则清空缓冲区,否则不清空。(剩余字符留在缓冲区等下一轮处理,所以如果输入"123abc",会发现程序进行了两次输入的行为,这是对的。)
除非有规定要一次处理多个输入,那就按具体规定来。
cin判断输入错误
cin本身在输入错误的时候会返回一个false值,也可以用来判断输入错误
cin.good()
反映输入完成后能否进行下一次读取。
cin.fail()
反映本次输入是否错误。
可能出现同为0的情况,不会出现同为1的情况。
同为0的情况:输入文件的末尾没有换行
1 | 12 34 56 |
不推荐使用cin.good()进行判断,推荐使用cin.fail(),这是一个仅专注于数据本身输入是否正确的判据
cin清除错误标记
cin.clear()
把异常标记全都清除,恢复正常状态
上面这些cin.good()、cin.fail()都会回到正确的情况下去
一定要先clear再ignore!!!!
cin清除缓冲区
cin.ignore(n, ch)
清理缓冲区,最大清空n个字符,一直遇到ch字符为止。ch一般填’\n’。
这里写一个随便想出来的大数显然是不合适的。目前最好的方法是#include <climits>(C++)或#include <limits.h>(C++/C),填INT_MAX(就是2147483647)进去。
在这个地方填其他的:
填cin.rdbuf() -> in_avail()
释义:查询输入流缓冲区中可立即读取的字符数
能不能用:不可以用,MSVC可以过,gcc不能用
填numeric_limits<streamsize>::max
释义:用于获取 streamsize 类型的最大值(不是输入流缓冲区容纳字符数上限,而是一个逻辑上的上限,非常非常大,在我这里是9223372036854775807)
能不能用:可以用,有限制,当你在Visual Studio下#include <Windows.h>的时候会和那个库里的一个max冲突。如果要在这个情况下强行共存,可以定义一个宏#define NOMINMAX。但这样一个代码就不适用多个编译器,不好。
getchar()循环
C++能用这个,但是还是ignore成员函数更好。
见下面的scanf清除缓冲区
cin输入错误示例
1 | while(1){ |
scanf判断输入错误
没有状态位,对于输入超限这种情况没有处理能力!
核心方法:看scanf函数返回值是不是等于输入数据的个数。
把输入x和判读输入错误合到一起:
1 | if(scanf("%d", &a) == 0){ |
scanf不需要清除错误标记,因为没有错误标记
scanf清除缓冲区
getchar()循环
直接用这个
1 | while(getchar()!='\n') |
这一种也可以:
1 | while((ch = getchar())!='\n' && ch != EOF) |
其他方案:
scanf("%*[^\n]")
释义:读取字符并丢弃,直到’\n’。这个*是丢弃的意思,中括号是一个匹配表达式,^是取反的意思,代表匹配不是’\n’的字符。
能不能用:可以
setbuf(stdin, NULL)
释义:关闭输入缓冲区。
可不可以用:错误的东西,不能用
rewind(stdin)
释义:将缓冲区光标移到开头,重新开始写,覆盖之前的内容。
可不可以用:不推荐。在目前的情况下没什么问题,但是到linux上会不支持。
scanf输入错误处理示例
1 | while(1){ |