C++中cin对象关于错误的处理
在C++中 iostream类中的cin对象是 “智能” 的,意味着它可以根据变量的类型自动限制输入 ,当我们输入的数据为相对类型或者可以根据C++标准自动转换为需要的变量类型(int转换为double)并不会发生什么,但是出现不可以转化的情况时 就有了问题
cin怎样检查输入
对于多字符 cin对象会跳过空白(换行符,制表符,空格) 直到遇到非空白字符,对于单字符,也是一样,直到cin对象把一个合法数据放进变量。
我们首先来看一个例子
我们可以给一组数据< 200 10 -50 123x 150 >
int input;
while(cin >> input)
{
sum+=input;
}
cout << sum << endl;
答案是什么呢 283 为什么会出现这样一个数字呢 我们可以看到答案就是前三个元素加上第四个违规字符串的数字部分 这到底是为什么呢 原因是C++引入了 流 这个概念 ,我们把输入看做字节流,并把当遇到违规字符时返回“错误”(这个“错误”究竟是怎么回事我们后面会讲)并把前面已经在字节流中的字符转化为4字节int型(本例中),然后x将留在输入流中 供下一个cin对象读取(这里还有一点小问题),在这个例子里面x就相当于是违规字符,前面的1 2 3 就相当于是整数的有效位,从而把1 2 3转换成123放入变量,x继续在输入流中,同时把流的状态转化为failbit
流的状态
通常我们有四个状态
- eofbit:通常被设置于cin到达文件尾时发生的错误
- failbit:cin未能操作到预期字符(例如本文中的例子) 或者试图读取不可访问的文件或者受保护的磁盘时
- badbit :无法诊断的失败破坏流
- goodbit:以上三种没有发生
所以我们判断流的状态要么用上面三种 要么用下面一种 这很好理解
说了这些 这四种状态究竟是什么呢 其实他们是cin或者cout对象中的一个数据成员,从ios_base中继承而来,刚开始的时候被初始话为goodbit 当遇到异常时(以上三种异常)把goodbit设置为0 所遇到的异常的那一个状态设置为1 ,其实我们只需要把这些状态理解为开关(专业一点叫做位),刚开始是goodbit开关被打开,意味着我们可以输入,当遇到异常是goodbit被关闭,其他三个开关之一被打开,这个时候我们不能够输入。
如何设置状态
其实一般情况下就是我们如何去使得goodbit被设置为0的cin对象如何再次接收对象,这个时候我们有了两种方法
- clear():将流的状态强制覆盖为参数所代表的状态
函数原型:void clear(iostate state = goodbit) - setstate():将括号内的状态叠加到原始状态上
知道了如何设置状态 我们又如何去使用呢 这里有几个返回值为bool型的方法
- good() 输入流可以使用时为true 即goodbit状态位为1
- eof() :eofbit被设置返回1
- bad():badbit被设置返回1
- fail():failbit被设置返回1
那我们该如何使用这个状态呢 想想看 我们有时程序如果莫名其妙崩掉(假设它的错误原因就是cin这里!)我们肯定希望知道是为什么,究竟是读到了文件尾,还是文件的数据有问题,又或者是文件根本不可读,我们该怎么办呢,流的状态就派上了大用场,还有一点,就是当流的状态被改变时,流将会对后面的输入或输出关闭,而且不匹配的输入任然在流中,对于第一点不难理解,因为其中goodbit已经被设置为0,我们当然无法使用这个流,所以我们需要重新设置流的状态,对于第二点,当遇到异常时流的状态被改变,从而数据停止从流传向我们希望它传向的方向(文件,变量)。
对于这些情况我们该怎么办呢 一个函数加一个循环就可以解决
int input;
int sum=0;
while(cin >> input){
sum+=input;
}
cout <<"sum = "<<sum <<endl;
if(cin.fail() && !cin.eof()) //failbit==1
{
cin.clear();
while(cin.get()!='\n'){
continue;
}
}
/* {
cin.clear();
while(!isspace(cin.get())){
continue;
}
} */
//以上这种方法其实也可以
else
{
cout << "eofbit == 1\n";
exit(1);
}
cin >> input;
cout << input << endl;
可以看到这个代码针对了出现错误的两种情况(fail()在failbit 或者 eofbit 被设置时都被返回true)都进行了判断,我们来解释下这段代码,假如我们有这样一组数据 <2000 8 1 6 18zsy 6 6>,根据文章上面的解释,我们可以知道这个时候答案为 2033 ,failbit被设置为1,输入流中存在的的数据为<zsy 6 6 ‘\n’>,然后我们进行循环中的操作,直到读到’\n’,退出循环,此时流就是一个崭新的流了。
其实很多情况下我们经常会使用这个清除流中数据的语句,所以我们可以把它写成一个内联函数,方便使用
inline void eatline(){while(cin.get()!='\n') continue;}