重载的抽取操作符 >>
istream类(在头文件iostream中定义)重载了抽取操作符 >> ,使之能够识别下面的这些基本类型:
signed char & unsigned char &
char & short &
unsigned short & int &
unsigned int & long &
unsigned long & float &
double & long double &
这些操作符函数被称为格式化输入函数(formatted input functions) ,因为它们可以将输入数据转换为目标指定的格式。
典型的操作符函数的原型如下:
istream & operator>> (int &);
参数和返回值都是引用。因此cin能够直接修改用作参数的变量的值。
可以将hex、oct和dec控制符与cin一起使用,来指定将整数输入解释为十六进制、八进制还是十进制格式。例如:
cin >> hex;
将输入12或0x12解释为十六进制的12或十进制的18,而将ff或FF解释为十进制的255。
该类还为下列字符指针类型重载了>> 抽取操作符:
signed char * char * unsigned char *
对于这种类型的参数,抽取操作符将读取输入中的下一个单词,将它放置到指定的地址,并加上一个空值字符,使之成为一个字符串。
每个抽取操作符都返回调用对象的引用,这使得能够将输入拼接起来,就像拼接输出那样:
char name[20];
float fee;
int group;
cin >> name >> fee >> group;
cin>> 如何检查输入
cin>> 跳过空白(空格、换行符和制表符),直到遇到非空白字符。
在单字符模式下,>>操作符将读取该字符,将它放置到指定的位置。
在其他模式下,>>操作符将读取一个指定类型的数据。它的读取从非空白字符开始,到与目标类型不匹配的第一个字符之间的全部内容。
当输入未满足程序的期待时,抽取操作符将不会修改参数的值,并返回0(false)。
流状态
cin或cout对象包含一个描述流状态(stream state) 的数据成员(从ios_base类那里继承的)。
流状态被定义为iostate 类型,而iostate是一种bitmask 类型。由3个ios_base元素组成: eofbit 、badbit 和failbit ,其中每个元素都是一位,可以是1(设置)或0(清除)。
当cin操作到达文件末尾时,它将设置eofbit;
当cin操作未能读取到预期的字符时,它将设置failbit。
I/O失败(如试图读取不可访问的文件或试图写入受保护的磁盘),也可能将failbit设置为1。
在一些无法诊断的失败破坏流时,badbit元素将被设置。
当全部3个状态位都设置为0时,说明一切顺利。
下图列出了这些位和一些报告或改变流状态的ios_base方法。
1. 设置状态
clear() 方法将状态设置为它的参数。例如:
clear();
将使用默认参数0,清除全部3个状态位。
clear(eofbit);
将状态设置为eofbit;也就是说,eofbit将被设置,另外两个状态位被清除。
setstate() 方法只影响其参数中已设置的位。例如:
setstate(eofbit);
将设置eofbit,而不会影响其他位。
2. I/O和异常
exceptions() 方法用来控制异常如何被处理。
exceptions()方法返回一个位字段,它包含3位,分别对应于eofbit、failbit和badbit。
修改流状态后,clear()方法将当前的流状态与exceptions()返回值进行比较。如果在返回值中某一位被设置,而当前状态中的对应位也被设置,则clear()将引发ios_base::failbit异常。如果两个值都设置了badbit,将发生这种情况。
如果exceptions()返回goodbit ,则不会引发任何异常。
os_base::failbit异常类是从std::exception类派生而来的,因此包含一个what()方法。
exceptions()的默认设置为goodbit,不会引发异常。
重载的exceptions(iostate)函数使得能够控制其行为:
cin.exceptions(badbit); // setting badbit causes exception to be thrown
位操作符OR (| )使得能够指定多位。例如:
cin.exceptions(badbit | eofbit);
如果badbit或eofbit随后被设置,将引发异常。
3. 流状态的影响
只有在流状态良好(所有的位都被清除)的情况下,才能正确的返回true或false。
设置流状态位有一个非常重要的后果: 流将对后面的输入或输出关闭,直到位被清除。
例如下面的代码不能工作:
while(cin >> input)
sum += input;
cout << "sum = " << sum << endl;
cin >> input; // won't work
然而,仅仅使用clear()方法还不足以重新设置流状态。因为导致输入循环终止的不匹配输入仍留在输入队列中,程序必须跳过它。
一种方法是一直读取字符,直到到达空白为止:
while( !isspace(cin.get() ))
continue; // get rid of bad input
isspace()函数是一个cctype函数,它在参数是空白字符时返回true。
另一种方法是,丢弃行中的剩余部分,而不仅仅是下一个单词:
while(cin.get() != '/n')
continue; // get rid of line
假设循环是由于到达文件尾或者由于硬件故障而终止的,则上面的错误处理代码将毫无意义。
可以使用fail() 、eof() 或bad() 方法来判断假设是否成立。
其他istream类方法
get(char&) 和get(void) 方法提供不跳过空白的单字符输入功能。
get(char*, int, char) 和getline(char*, int, char) 函数在默认情况下读取整行而不是一个单词。
它们被称为非格式化输入函数(unformatted input functions) 。
1. 单字符输入
使用char参数或没有参数的情况下,get()方法读取下一个输入字符,即使该字符是空格、制表符或换行符。
get(char&) 版本将输入字符赋给其参数。
get(void) 版本将输入字符转换为整型(通常是int),并将其返回。
由于get(char&)版本的读取方式,便可以使用下面的判断语句:
char ch;
cin.get(ch);
while(ch != '/n')
...
假如改为cin >> ch 循环将不会停止。
get(char&)成员函数返回一个指向用于调用它的istream对象的引用,这意味着可以拼接输入:
char c1,c2,c3;
cin.get(c1).get(c2) >> c3;
如果cin.get(char&)到达文件尾——无论是真正的,还是通过键盘仿真的文件尾(DOS为按下<Ctrl>+<Z>;UNIX为在行首按下<Ctrl>+<D> ),它都不会给其参数赋值。该方法还调用setstate(failbit),导致cin的测试结果为false。
get(void)成员函数也读取空白,但使用返回值来将输入传递给程序。例如:
char ch;
ch = cin.get(); // use return value
因此,试图拼接该方法来输入,将是错误的。
如果到达文件尾后(真正的或者仿真的文件尾都一样),该方法将返回值EOF ——iostream头文件的一个符号常量。
int ch;
while( (ch = cin.get() ) != EOF)
...
这里应将ch的类型声明为int,而不是char。因为值EOF可能无法使用char类型来表示。
下图总结了这两个版本的特性。
2. 字符串输入
istream & get(char *, int, char);
istream & get(char *, int);
istream & getline(char *, int, char);
istream & getline(char *, int);
上述中的第一个参数是用于放置输入字符串的内存单元的地址。
第二个参数比要读取的最大字符数大1,额外的一个字符用于存储结尾的空字符,以便将输入存储为一个字符串。
第三个参数指定用作分界符的字符,只有两个参数的版本将换行符用作分界符。
上述函数都在读取最大数目的字符或遇到分界符后为止。
get()和getline()之间的主要区别在于,前者将分界符(默认为换行符)留在输入流中,这样接下来的输入操作首先看到的将是分界符,而getline()抽取并丢弃输入流中的分界符。
ignore() 成员函数接受两个参数。原型如下:
istream & ignore(int =1, int = EOF);
第一个参数是数字,指定要读取的最大字符数。
第二个参数是字符,用作输入分界符。例如:
cin.ignore(255,'/n')
上述语句将读取并丢弃接下来的255个字符或直到到达第一个换行符。
该函数返回调用对象,这使得能够拼接函数调用,例如:
cin.ignore(255,'/n').ignore(255,'/n');
3. 意外字符串输入
get(char *, int)和getline()如果不能抽取字符,它们将把空值字符放置到输入字符串中,并使用setstate()设置failbit。
如果遇到文件尾,将设置eofbit,如果遇到流被破坏(如设备故障),将设置badbit。
对于get(char *, int)来说,如果输入了一个空行,将导致不能抽取字符,例如:
char temp[80];
while(cin.get(temp, 80) // terminates on empty line
...
但空行并不会导致getline()设置failbit。因为getline()仍将抽取换行符,虽然不会存储它。
如果希望getline()在遇到空行时终止循环,则可以这样编写:
char temp[80];
while(cin.getline(temp,80) && temp[0] != '/0'); // terminates on empty line
假设队列中的字符数等于或超过了输入方法指定的最大字符数。例如:
char tmep[30];
while(cin.getline(temp,30) );
如果读取了29个字符,并且输入流中的下一个字符不是分界符,则设置failbit。
对于get(char *, int)方法,它首先测试字符数,然后检测是否为文件尾以及下一个字符是否是分界符。如果它读取了最大数目的字符,则不设置failbit标记。
可以用peek() 来查看下一个输入字符,如果它是换行符,则说明get()已读取了整行;否则说明是在到达行尾前停止的。
这种技术对于getline()不适用,因为该方法读取并丢弃换行符,因此查看下一个字符无法知道任何情况。
其他istream方法
read() 函数读取指定数目的字节,并将它们存储在指定的位置中。例如:
char gross[144];
cin.read(gross,144);
该方法不会在输入后加上空值字符,因此不能将输入转换为字符串。
它常与ostream write() 函数结合使用,来完成文件输入和输出。
该方法的返回类型为istream & 。
peek() 函数返回输入中的下一个字符,但不抽取输入流中的字符。
gcount() 方法返回最后一个非格式化抽取方法读取的字符数。
这意味着字符是由get()、getline()、ignore()、read()方法读取的,而不是由抽取操作符(>>)读取的。
putback() 函数将一个字符插入到输入字符串中,被插入的字符将是下一条输入语句读取的第一个字符。
该方法接受一个char参数——要插入的字符。
返回类型为istream & 。