欢迎来到博主的专栏:c++编程
博主ID:代码小豪
文章目录
- 标准IO流
- 标准输入输出
- cin
- cin的四个状态标志
标准IO流
<iostream>库中的IO类在之前已经经常使用了,但是我们还从未仔细的了解过。在c++标准库中,IO类分为两种,分别是输入流istream类,输出流ostream类。
cin是istream类的对象。从标准输入流中读取数据
cout是ostream类的对象,向标准输出流写入数据
cerr是ostream类的对象,向标准错误流写入数据。
IO流又细分为多种,我们先从标准IO流开始讲起
标准输入输出
cin
cin从标准输入流(通常是键盘)当中读取数据,并将数据写入缓冲区当中,当程序需要从输入流当中提取数据时,就会在缓冲区当中进行读取。
那么什么是缓冲区呢?我们可以将缓冲区想象成一个动态开辟的空间(或者是一个不显示的字符数组),当我们使用键盘输入时,实际上是输入在输入缓冲区当中,当我们敲下回车时,缓冲区的数据才会输入进程序当中。
为什么要设计出缓冲区呢?我们可以设想如果直接将键盘的数据直接写入在程序当中,第一,效率会变低,因为如果一个键一个键的读入,那么CPU就会进行大量的低级I/O操作,而使用缓冲区之后,从每个数据的读入,变成了一次性的多个数据读入,这会快很多。
第二,如果我们发生了错误写入操作,那么就可以在缓冲区当中对数据进行删除重写。方便操作。
cin的四个状态标志
从缓冲区写入到程序当中是会发生写入成功和写入失败这两种情况的。由于写入到缓冲区的数据不会进行区分,所以只有从缓冲区写入到程序当中就有可能出现错误了,最典型的例子就是写入的数据,与接收数据的变量的类型不匹配。而c++将cin写入程序的情况分为了4种,也就是四种状态标志。
(1)good
(2)eof
(3)fail
(4)bad
good标志说明写入成功,eof标志则是一个结束标志,表示写入操作已经结束,fail和bad标志则说明写入操作发生了错误。
而cin对象被设置了不同的状态,也会有不同的效果,在默认情况下,cin的标志为good,表示写入正常,此时cin会继续读取缓冲区的内容,而如果cin处于其他标志,则不会继续向程序写入数据。
为了让用户了解到cin对象的当前的状态,设计了四个函数。
bool good() const;
bool eof() const;
bool fail() const;
bool bad() const;
这四个函数的使用方法也很简单,如果cin设置了good标志,那么good标志就会返回true,反之为false,同理,其余函数也是以这种方式来观察当前cin对象的状态。
我们可以通过下面程序观察cin的写入情况:
void test1()
{
int i = 0;
while (cin >> i)
{
cout << cin.good() << " " << cin.eof() << " " << cin.fail() << " " << cin.bad() << endl;
cout << i<<endl;
}
cout << cin.good() << " " << cin.eof() << " " << cin.fail() << " " << cin.bad() << endl;
cout << i << endl;
}
这个while(cin>>i)在I/O型OJ当中常见的多行写入的程序。但似乎我们好像很少会去深究这段代码的真正的意义,现在,我们来正式的了解一下。
众所周知(如果你不知道,那么现在你也知道了),while当中的表达式结果是bool类型的,而c++标准库为istream对象重载了一个operator bool的函数。我们可以在cpp网站上看到这个函数的定义。
如果istream对象(比如cin),被设置了bad和fail标志,那么就会返回false,其余情况就会返回true。所以如果cin发生写入错误时或者遇到结束标志(eof)时,就会结束循环。
因此我们可以利用这段代码来分析什么情况下cin的错误标志会被设置。
情况(1)正常输入:
我们输入1111,由于接收1111的类型会是int类型,因此设置的标志会是good、程序正常读入数据。
此时cin的状态为:good(1),eof(0),fail(0),bad(0)。(1为ture,0为false,表示cin对象当前处于为1的状态,在本例当中,即cin对象处于good状态。)
情况2(2)设置了结束标志。
在window当中,缓冲区的结束标志eof,可以使用键盘ctrl+z进行写入,用^z表示eof,比如我们尝试一个多行输入
1111
2222
^z
由于cin读取到了结束标记eof,此时cin的状态为good(0),eof(1),fail(1),bad(0)。根据前面所说的,由于istream对象被设置了fail或者bad标志,因此while(cin>>i)的结果为false,此时结束多行输入。
(3)写入错误(fail)
当写入的数据与接收数据的对象类型不匹配时,就会发生写入错误。比如我们写入ss。
由于ss是char类型的数据,因此不能用int类型的变量来接收数据。此时cin的状态为good(0),eof(0),fail(1),bad(0)。while(cin>>i)的结果为false,结束循环。要注意,一旦cin的状态不再是good,那么cin不会再从标准输入流当中读取数据。关于cin的操作也会停止。
void test2()
{
int i = 0;
cin >> i;
cout << cin.good() << " " << cin.eof() << " " << cin.fail() << " " << cin.bad() << endl;
cout << i << endl;
int j = 0;
cin >> j;
cout << cin.good() << " " << cin.eof() << " " << cin.fail() << " " << cin.bad() << endl;
cout << j << endl;
}
如果我们对i进行写入的时候发生了错误(比如fail或者eof,bad标志被设置时),此时cin就会变得不可用。如果想要重新启用cin,那就要恢复cin的标志。将cin置为good即可。c++设计了两个函数用于重新设置标志码,为:
setstate是将一个标志码设置到cin当中,而clear则是恢复cin的默认状态,即(good(1),eof(0),fail(0),bad(0))。
void test2()
{
int i = 0;
cin >> i;
cout << cin.good() << " " << cin.eof() << " " << cin.fail() << " " << cin.bad() << endl;
cout << i << endl;
cin.clear();
int j = 0;
cin >> j;
cout << cin.good() << " " << cin.eof() << " " << cin.fail() << " " << cin.bad() << endl;
cout << j << endl;
}
但是这样做也不能解决问题,这是因为缓冲区中的s由于读取失败,因而还存在与缓冲区当中,继续读取并不能解决问题,因此我们要先将缓冲区当中的字符清理掉。因此正确的方式为:
(1)恢复标志
(2)将缓冲区中的字符全部读取
void test2()
{
int i = 0;
cin >> i;
cout << cin.good() << " " << cin.eof() << " " << cin.fail() << " " << cin.bad() << endl;
cout << i << endl;
if (cin.fail())
{
string str;
cin.clear();
cin >> str;
}
int j = 0;
cin >> j;
cout << cin.good() << " " << cin.eof() << " " << cin.fail() << " " << cin.bad() << endl;
cout << j << endl;
}
情况4:
bad标志是由于缓冲区本身的错误而导致的,因此恢复错误码并清理缓冲区并不能解决问题。而是要从程序当中入手,看看程序当中有没有导致缓冲区错误的代码。