Cpp输入方式
在算竞中经常需要读取数据,这篇文章对Cpp的输入方式总结。
输入方式
1.cin
(1)cin>>等价于cin.operator>>(),即调用成员函数operator>>()进行读取数据。
(2)当cin>>从缓冲区中读取数据时,若缓冲区中第一个字符是空格、tab或换行这些分隔符时,cin>>会将其忽略并清除,继续读取下一个字符,若缓冲区为空,则继续等待。但是如果读取成功,字符后面的分隔符是残留在缓冲区的,cin>>不做处理。如果后续接getline()要记得处理换行符!
补充:
-
Tab键:通常在键盘上标记为
Tab
,按下后插入一个制表符(Tab字符)。制表符的长度通常为多个空格,可以在不同的编辑器或环境中设置长度,常见的设置是4个或8个空格。【在计算机中用于对齐文本的特殊字符】 -
空格键:按下后插入一个空格字符。每次按下空格键插入一个空格,长度固定。
(3)不想略过空白字符,那就使用 noskipws 流控制。比如:cin>>noskipws>>input;
示例1:
1 |
|
补充:
C 库函数 int isspace(int c) 检查所传的字符是否是空白字符。
标准的空白字符包括:
1 | ' ' (0x20) space (SPC) 空格符 |
示例2:
1 |
|
输入
1 | q w e a z |
输出
1 | q w e a z |
2.cin.get()
单用cin.get()只能读取其中一个字符,不能存储为string全部的字符。
若要存储全部则必须传入一个数组以及长度。
1 |
|
3.cin.getline()
接受一个字符串,可以接收空格并输出,吃掉末尾的换行符的。
1 |
|
在调用 cin.getline(m, 5)
时,函数从输入流中读取最多 5 - 1 = 4
个字符,并在读取到的字符后添加一个终止空字符 \0
。
延伸:
cin.getline()实际上有三个参数,cin.getline(接受字符串的看中间的m,接受个数5,结束字符)
当第三个参数省略时,系统默认为’\0’ 。
如果将例子中cin.getline()改为cin.getline(m,5,‘a’);当输入jlkjkljkl时输出jklj,输入jkaljkljkl时,输出jk
当用在多维数组中的时候,也可以用cin.getline(m[i],20)之类的用法:
1 |
|
string输入
4.getline()
接受一个字符串,可以接收空格并输出,需包含“#include”
1 |
|
5.getchar()
函数原型:int getchar(void);
返回类型为int,参数为void
头 文 件:#include<stdio.h>
返 回 值:
- getchar返回的是字符的ASCII码值(整数)
- getchar在读取结束或者失败的时候,会返回EOF。(EOF意思是end of file,本质上是-1)
读取方式:只能输入字符型,输入时遇到回车键才从缓冲区依次提取字符。
结束输入的方式:以Enter结束输入(空格不结束),接受空格符。
舍弃回车符的方法:以Enter结束输入时,接受空格,会舍弃最后的回车符。
getchar函数执行过程详解
1 |
|
它的简单意思就是从键盘读入一个字符,然后输出到屏幕。理所当然,我们输入A,输出就是A,输入B,输出就是B。
那么我们如果输出的是ABC呢?答案是A。
解释如下:当我们从键盘输入字符‘A’,‘B’, ‘C’,并按下回车后,我们的输入被放入了输入缓冲区,这个时候getchar()会从缓冲区中读取我们刚才的输入,一次只读一个字符,所以字符A就被拿出来了,赋值给了ch,然后putchar()又将ch放在了标准输出,也就是这里的屏幕,所以我们看见了最终的显示结果A。同时字符‘A’也被缓冲区释放了,而字符‘B’,'C’仍然被留在了缓冲区。而这样是很不安全的,有可能下次使用的时候,我们的缓冲区会读到一些垃圾,但是当程序结束的时候,它会自动刷新。
解释:现在,考虑这样一个场景:你的程序在接收了用户的一些输入之后,由于某种原因突然终止了,没有正常退出。这种情况下,这些未处理的输入可能会留在输入缓冲区中,没有被清理掉。当你的程序下次运行并再次试图从输入缓冲区获取输入时,它可能会先读取到这些残留的旧数据,而不是新的用户输入。这就是我们所说的"可能会读到一些垃圾"的含义。
6.gets()
1 | gets(m)用于string类的,需包含 |
可用于多维数组。
可用于多维数组。
1 |
|
算竞输入补充
我利用代码生成了三千万个【1,100】以内的随机数,并且将分别用cin
和scanf
输入到一个数组中。
1 | const int MAXNUM = 30000000; |
开始测试
1 | int start = clock(); |
1 | for(int i = 0;i < 100000; i++) |
我在Windows系统GCC编译器下得出的用时数据(单位:秒),且已验证过不存在偶然性:
scanf | cin |
---|---|
6.836 | 11.303 |
同时,我用printf和cout分别输出十万个正整数。
printf | cout |
---|---|
6.172 | 11.509 |
于是可以得出结论:cin&cout的确在效率上是低于scanf&printf的
而我们可以通过std::ios::sync_with_stdio(false);指令关闭同步(这里要注意:当关闭同步之后为了确保准确,不要使用 printf&scanf了),除此之外我们还可以通过std::cin.tie(nullptr);获取cin更优的性能(解除std :: cin和std :: cout之间的绑定,来降低IO的负担使效率提升)。那么在对iostream优化之后的 printf&scanf 和 cin&cout效率差距又有多大呢?我通过上述的实验得出以下数据:
1 |
|
输出
1 | world!Hello, |
Note:std::ios::sync_with_stdio(false);
这条语句的作用是关闭C++的iostream和C的stdio之间的同步。
在默认情况下(同步开启),程序的输出会是 “Hello, world!”。因为cout和printf是同步的,所以他们的输出顺序是按照我们写的顺序来的。
但是,如果我们关闭同步,那么程序的输出就可能不是"Hello, world!"了。可能cout的输出先出现,也可能printf的输出先出现,这取决于哪个操作更快。所以,在关闭同步后,我们就不能再预期混合使用C++的iostream和C的stdio时的输出顺序了。
疑问:关闭C++的iostream和C的stdio之间的同步为什么能提高速度?
有趣的解答:
假设你正在举办一场派对,你邀请了两个DJ,一个是专门播放摇滚音乐的,另一个是专门播放爵士乐的。你希望他们能够交替播放音乐,这样你的派对就可以同时享受到摇滚和爵士的风格。
但是,为了确保他们能够顺利地交替播放,你需要找一个协调员来时刻注意两位DJ的状态,告诉他们什么时候该停,什么时候该播。这个协调员就相当于同步机制,他确保了摇滚DJ和爵士DJ(相当于iostream和stdio)能够和谐工作。
然而,这个协调员并不是免费的。他需要时间去观察和指挥,这就可能会延迟音乐的切换,也就是说,派对的整体节奏可能会因为等待协调员的指示而变慢。这就像为了维持iostream和stdio的同步,系统需要做额外的工作(比如刷新缓冲区),这会导致效率的降低。
现在假设你决定不再需要协调员,让两位DJ自己决定何时播放音乐。这样,你就节省了协调员的成本,派对的节奏也可能会更快。但是,你也无法再保证摇滚音乐和爵士乐能够完美交替,他们可能会同时播放,也可能会有短暂的静音。这就像当你关闭iostream和stdio的同步时,虽然提高了效率,但也无法保证他们的操作顺序。
总的来说,关闭iostream和stdio的同步可以提高效率,但同时也会牺牲他们的协调性。在某些情况下,这可能是值得的,但在其他情况下,可能就需要考虑其他解决方案了。
scanf | cin(优化后) |
---|---|
6.836 | 2.695 |
printf | cout (优化后) |
---|---|
6.172 | 6.178 |
可以看见在关闭同步之后cin的效率已经是高于scanf了,并且cout的速度与printf的速度也相差无几,那我们还能不能继续优化呢?
我们注意到通常在用cout输出的时候,更习惯去使用endl,它既可以达到换行的需求又可以刷新缓冲区,然而在如此高度的循环下它一直对缓冲区的操作却降低了效率,所以将endl换成’\n’cout的效率将会起飞
(顺带一提,我在开启同步的条件发现endl或是\n
对cout效率的影响并不大,不知道是不是我的问题,希望各位指出)
printf | cout(‘\n’) |
---|---|
6.172 | 1.106 |
如此一来我们再一次得出结论:在同步开启时,scanf&printf的效率要高于cin&cout;当同步关闭时,cin&cout的效率要高于scanf&printf。
最后,附上ACMer喜欢的快读模板
1 | /*快读快写*/ |
read | cin |
---|---|
0.485 | 2.695 |
write | cout |
---|---|
18.908 | 1.106 |