c++ istream 详解
c++头文件<iostream>
中定义了istream
流,提供了基本的输入方法cin
cin
cin
对象非常常见,基本上每一个学习c++的人开始使用的读取方式都是cin >>
,该方法可以从输入缓冲区连续的读取,并以换行,空格,TAB为分隔符,例如:
int n1,n2,n3;
cin >> n1 >> n2 >> n3;
cout << n1 << " " << n2 << " " << n3;
如果我们的输入如下所示:
1 2 3[回车]
或者
1
2
3
则对应的输出为
1 2 3
看起来很好理解,我们输入1 2 3时,以空格或者回车作为分隔符,cin
则按照分隔符将其分三次读取,存入变量中,而且似乎可以自动匹配变量类型,但此处有一个小问题,做为分隔符的回车和空格会如何处理呢?
cin.get()
讨论上面的问题之前我们先来看一个新的读取方法cin.get()
,该方法的作用是读取缓冲区的下一个字符,其重载主要为四种
int cin.get();//返回读取的字符的ASCII码
istream& cin.get(char& var);//传入一个字符,读取后保存在该字符中,同时返回istream对象
istream& cin.get ( char* s, streamsize n );//传入char[](c风格字符串),同时指定目标空间大小
istream& cin.get ( char* s, streamsize n, char delim ) //前俩个参数同上,最后指定一个字符,当读取到该字符时停止,默认为换行符
先来看不带参数的cin.get()
该方法的作用是读取下一个字符,因此无论下一个是空格,换行,还是回车都会被读取,同时返回读取到的字符的ASCII码,这个应该很好理解
然后是cin.get(char& var)
该方法传入一个字符变量的引用,读取到的字符会保存在该变量中,因此其无非就是无参方法的一个变种,区别是该方法返回了istream对象,所以会由下面这种写法
char ch1,ch2;
cin.get(ch1).get(ch2);
相信我,你不会想这么写的
cin.get(char* s,streamsize n)
这个方法就有所不同,他传入的第一个参数是c风格的字符串(char类型数组),后面的int类型则指定了读取的长度,所以该方法是可以是可以读取空格的,但是它依然默认以换行符结束
cin.get(char* ch, streamsize n,char delim)
该方法多了一个参数,delim指定了截止字符,该方法读取到该字符时便终止读取,默认为换行符,同时需注意,戒指字符是不会被丢弃的,它会被遗留在缓冲区中
在这里我们可以讨论刚才的问题了,看下面的代码
int num;
char ch;
cin >> num >> ch;
cout << num << " " << ch;
我们的输入为
14564 ffd
毫无疑问输出应该是
14564 ffd
原因很简单,cin>>识别了int型,读取了完整的数字,同时识别了char型,读取了一个字符,如果换成下面这样代码
int num;
char ch;
cin >> num;
ch = cin.get();
cout << num << " " << ch;
很显然,两段代码的输出是一样的
一样的对吗?
那必然是不对了!这次输出应该是
14564
是的,后面的没了,准确的说不是没了,而是输出了空格
我的意思是,ch里存的是个空格
哪里来的空格,那必然是cin>>留下来的
没错,cin>>方法不会读取或丢弃分隔符,而是留在缓冲区中。
那为什么第一段代码没出错呢?那当然是因为第一段代码都是使用cin>>方法读取,大家都是一家人,互相知道对方不喜欢擦屁股,所以cin>>如果首先读取到分隔符便会把他们丢弃掉,直到获得有效输入为止
我觉得这样做是为了让人们全部使用
cin>>
而不是cin>>
orcin.get()
or 接下来的cin.getline()
混合使用但其实
cin.get(char*, streamsize, char)
方法也会把截止字符留在缓冲区,难道这是c++的特色?
cin.getline()
该函数有两个重载
istream& getline(char* s, streamsize count); //默认以换行符结束
istream& getline(char* s, streamsize count, char delim);
不必多解释了,一样的参数表
但它与前面的区别就是,它会把截止字符扔掉,这很棒(大概吧)
当然除此之外还有一个我们一直都没有提过的不同点,这涉及到了cin的另一个特性,下面我们就来说说吧
cin错误
用户输入并不可能总是正确的,因此我们在写程序的时候需要进行合法性验证,但如果我们不小心读取了错误的输入会发生什么呢?
int num;
cin >> num;//输入一个数字
cout << num;//输出
在这个例子里,我们要求输入一个数字,保存在int类型中,但是如果我们错误的输入了一个字符,会怎么样呢?
经过尝试我们发现,如果我们输入一个字符保存在int类型中,它的值是随机的(某些编译器会自动初始化变量,因此可能会始终为零),这说明我们的输入并没有被保存,cin读取失败了
cin对象提供了一个方法来检测输入流是否正常,cin.fail()
在输入流正常时返回false
,出现错误时返回true
int num;
cin >> num;//输入一个数字
cout << cin.fail() << num;//输出
这个时候我们就会看到cin.fail()
返回了true,说明输入流出现了错误,程序自动停止了输入
这个时候就需要我们手动的修正错误,来重新获取输入,这里就需要用到另外几个方法
cin.clear()//清除错误标识符
cin.ignore()//抛弃缓冲区中下一个字符
cin.sync()//清空整个缓冲区
这里提到了一个新的概念,错误标识符,这是cin用来表示输入错误的的属性,一共有四种,这里我们只用到其中的一种,即failbit标识符,当输入存在错误时,该标识符便会被设置为1,此时cin便会停止,而我们使用cin.clear()
便可以清楚标识符,让cin恢复,同时需要注意的是,我们要记得抛弃掉错误的输入,因为原先存在的错误输入依然留在缓冲区中,所以我们使用cin.ignore
或者cin.sync
来清除掉错误的输入,而具体使用哪一个则视具体情况而定
int num;
cin >> num;//输入一个数字
cout << cin.fail() << num;//输出
cin.clear();//清除标识符
cin.ingore();//抛弃错误字符
cin >> num;
cout << num;
此时就可以恢复正常的输入
上面说到cin.getline()
还有的一个特性,便和cin错误有关
我们使用cin.get()
或者cin.getline()
读取输入的时候总会面临一个问题,由于我们使用c风格字符串来保存,因此不可避免地会出现我们输入的信息的长度比我们所定义的字符串长度要长,这个时候两种不同的读取方法便给出了不同的处理方案
cin.get()//读取至最大长度后,将多余的信息留在缓冲区
cin.getline()//检测到输入长度大于指定长度时,设置fialbit为1,停止读取
这便是两者的区别了,cin.get()会无视长度,能读多少读多少,多的就扔在那里不管了
而cin.getline()则会告诉程序这里有错误,输入非法,同时停止cin的行为,交给程序来处理