C++知识积累:cin、getline解析

在进行cin和getline的分析之前,一个必须清楚的是标准输入缓冲区,用户从键盘输入字符串,通过换行符将输入的字符串写入到输入缓冲区中,这里需要特别注意的是,将字符串写入输入缓冲区时会将末尾的换行符一起写入输入缓冲区,什么意思呢?

举个例子,在控制台中输入“abcd”然后回车,这时候写入输入缓冲区的字符串实际上是“abcd\n”,会一同将最后的回车换行符一起写入输入缓冲区。cin、getline等实际上都是对输入缓冲区进行操作的。

1. cin简介

cin是C++编程语言中的标准输入流对象,即istream类的对象。cin主要用于从标准输入读取数据,这里的标准输入,指的是终端的键盘。常用的cin方法用cin>>、cin.get以及cin.getline。

1.1 cin>>

总结:
以cin>>a为例,从输入缓冲区中读取数据,遇到space(enter、tab)时读取结束,并将space(enter、tab)之前的数据写入a中,但是会将space(enter、tab)遗留在输入缓冲区中

另一方面,如果输入缓冲区中的数据以space(enter、tab)开头,那么cin>>会抛弃掉这些space(enter、tab),直到遇到非space(enter、tab)的数据才进行读取,此时前面被抛弃掉的space(enter、tab)也不在输入缓冲区中了。

举个例子,如下所示:
在这里插入图片描述
这里的cin>>a;cin>>b;cin>>c和cin>>a>>b>>c;是同样的效果。一开始由于输入缓冲区中没有数据,cin会一直阻塞直到数据到来,输入“aaa+空格+bbb+换行+ccc”然后回车,这时实际上输入到缓冲区的字符串是“aaa bbb/nccc/n”;

输入缓冲区中有数据了,此时cin>>a开始将数据读入a中,遇到空格停止,那么实际上读入a的是“aaa”,此时缓冲区中剩下“ bbb/nccc/n”,特别注意这里bbb的前面是有一个空格的,此时cin>>a即执行完毕,开始执行cin>>b;

执行cin>>b时,由于输入缓冲区中有数据,因此cin>>b直接从输入缓冲区中读取数据,由于此时数据以空格符开头,因此cin>>会抛弃开头的空格符,然后读取数据直到遇到换行符/n停止,那么实际上读入b的是“bbb”,此时缓冲区中剩下“/nccc/n”,此时cin>>b执行完毕,开始执行cin>>c;

执行cin>>c同理,最终读入c的是"ccc",最终缓冲区中还剩下一个换行符“/n”。

补充说明

还有一点特别重要的是,cin>>的本质实际上是对cin对象的>>运算符重载,重载返回一个cin对象,其源码如下:

istream& operator>> (istream& is, char& ch );
istream& operator>> (istream& is, signed char& ch );
istream& operator>> (istream& is, unsigned char& ch );
istream& operator>> (istream& is, char* str );
istream& operator>> (istream& is, signed char* str );
istream& operator>> (istream& is, unsigned char* str )

因此cin>>a>>b实际上是先调用(cin>>a)>>b,其中cin>>a返回cin,然后再有cin>>b。

此外,cin>>还可用于真值判断,其实所有派生自ios的类都可以被强制转换为一个指针,如果设置了错误标志位则指针为null,否则非null。cin标志位一般有四种:ios::failbit,ios::badbit,ios::eofbit和ios::goodbit,在流输入正常的情况下,ios::goodbit为1,其余为0,当出现错误信息时ios::failbit,ios::badbit,ios::eofbit就都不一定为0了,
流的错误状态则由ios::failbit,ios::badbit,ios::eofbit组成,当错误标志位非0时,如图所示:
在这里插入图片描述

cin的错误标志位为rdstate,a为int型数据,前面输入的1234为int型,正常输入,failbit、badbit和eofbit均为0 ,此时rdstate为0;当输入"a"时,流发生错误,failbit被置为1,此时failbit、badbit和eofbit组成的rdstate错误标志位变成了4。
这四个标志位中,最常见的即是eofbit位,该标志位会在读取到文件末尾EOF或者Ctrl+Z时置位。

1.2 cin.get()

总结:cin.get()常用的有两种用法,一种用于读取一个字符a=cin.get()或cin.get(a),一种用于读取指定个数字符的cin.get(a,10),不管是哪一种,cin.get()是不存在遇到什么字符停止的,因为使用cin.get()的时候已经决定了获取字符的个数,要么1个要么就由用户指定个数,直接从输入缓冲区中读入相应个数的字符即可,不管这个字符是不是space(enter、tab),此外,如果输入缓冲区中开头即是space(enter、tab),cin.get()也不会抛弃这些字符,依然的照单全收。

1.2.1 cin.get()读取一个字符

cin.get(a)中a为char类型,a=cin.get()中a可以为string类型,举个例子:
在这里插入图片描述
这里一开始cin>>a阻塞等待输入缓冲区中的数据,然后输入“aaa+空格+bbb”然后回车,则将“aaa bbb\n”写入输入缓冲区中,此时输入缓冲区中有了数据,cin>>a就开始读取数据了,将"aaa"读入到a中,输入缓冲区中剩下“ bbb\n”;

然后接着的b=cin.get(),就直接读取下一个字符,即是“ ”,可见不带参数的cin.get()实际上与getchar()是类似的,此时输入缓冲区中就剩下“bbb\n”了,再进行cin.get©就将“b”读入到c中了。

1.2.2 cin.get()读取指定个数字符

在这里插入图片描述

如图所示,输入“ab+空格+cdefg+换行+hijk+空格+lmn+换行”,输入缓冲区中即是“ab cdefg\nhijk lmn\n”,此时执行cin.get(a,8),一定要注意,此时实际上读取到a中的是7个字符(包括中间的空格符,即使是换行符也会被读取)加上一个结束符‘\0’,因此a为“ab cdef”,此时缓冲区中就剩下“g\nhijk lmn\n”,再用cin>>b和cin>>c读取到"g"以及“hijk”。

1.3 cin.getline()

总结:cin.getline()与cin.get()来获取多个字符是相似的,不同的是,cin.getline()可以自己设置读取的结束符,即读取到结束符时停止读取,将结束符之前的写入到相应变量中,并将输入缓冲区中的结束符抛弃掉,如果不给定结束符,则默认以换行符‘\n’作为cin.getline()的读取结束标志符。

在这里插入图片描述
如图所示,先向缓冲区中输入“abcd efg\nhijklm\nnopq\n”,在调用cin.getline(a,10)时,与cin.get()类似,会企图从输入缓冲区中读入9个字符再加上一个终止符‘\0’,但是由于这里每个显式定义getline的读取结束符,则默认为’\n’,因此再读到g后面的换行符时,getline就停止了,此时就将“abcd efg”写入a中,并且将该换行符丢弃掉,此时输入缓冲区中就剩下“hijklm\nnopq\n”;

接着b=cin.get()读取下一个字符,可见b读取到的是‘h’,这也说明了前面的换行符已经被抛弃了,此时输入缓冲区中还剩下“ijklm\nnopq\n”;

然后继续执行cin.getline(c,10,‘x’),由于这里显式定义了结束符,因此遇到m后面的换行符时并不会停止读取,最终读取的9个字符为“ijklm\nnop”。

1.4 cin.ignore()

cin.ignore(int num)的用处在于忽略输入缓冲区中的某些字符,这些字符包括换行符、空格符等,如果不给定参数,则默认为忽略当前输入缓冲区中的第一个字符。

那么cin.ignore()什么时候用呢?比如说如下所示代码和运行情况:
在这里插入图片描述
看图里可知,cin>>会在输入缓冲区中留下空格符,所以当再用cin.getline或者cin.get()的时候读到的第一个字符就是空格符,但是这往往不是我们想要的,此时就可以再cin>>b后加上cin.ignore()来将缓冲区中的空格符去掉,这样就能达到我们的目的了。
在这里插入图片描述

2.getline()

实际上,我们常常会对整行数据读取,而要想读取整行数据,就不能对该行数据中的空格符space、制表符tab敏感,因此cin>>显然是不能满足要求的,而cin.get()和cin.getline()虽然不对空格符space、制表符tab敏感,但是需要预先指定存放数据的空间大小,由于实际的输入是不明确的,因此这里预留的大小往往都是较大的,这种方法也并不是太好,这时候getline()就能很好的解决这个问题。

getline()并不是cin中的成员函数,C++中定义了一个在std名字空间的全局函数getline,因为这个getline函数的参数使用了string字符串,所以声明在了头文件中了。getline利用cin可以从标准输入设备键盘读取一行,当遇到如下三种情况会结束读操作:
(1)文件结束;
(2)遇到行分隔符;
(3)输入达到最大限度。

getline(cin,str)函数是处理string类的函数。第二个参数为string类型的变量。读入时第二个参数为string类型,而不是char*,要注意区别
getline()函数的定义如下所示
1. istream& getline ( istream &is , string &str , char delim );
2. istream& getline ( istream &is , string &str );
其中,is是进行读入操作的输入流;str是用来存储读入的内容;delim是终结符,遇到该字符停止读取操作,不写的话默认为换行符。与cin.getline()相同,遇到终结符时会停止读取并且输入缓冲区中这个终结符也没有了。

getline()的真值与cin是相同的,所以在使用while(getline(cin,line))的时候,判断while()是否结束循环的条件不是输入流是否输入了回车(或getline函数里你自己定义的结束符),而是getline这个函数是否输入无效(这么说好像有点绕,给你们来个爽快的)。直到你输入了EOF或者ctrl+z,while循环才会结束,而且要注意的是,只有getline函数动作完毕时,while才会执行内部的循环,直到你输入了回车或者你自己设定的结束符,str这个字符串才会被输出。并且,还有一点需要注意的是,每次调用getline(cin,str)后,调用之前的str都会被新得到的数据所覆盖。
如图所示:
在这里插入图片描述
由图可知,getline(cin,str)是可以覆盖str中的内容的。

此外,getline(cin,str)中的cin可以换成其他的输入流,如stringstream,可见getline与stringstream

猜你喜欢

转载自blog.csdn.net/qq_28114615/article/details/85728452
今日推荐