C++ Primer第五版笔记——特殊的IO库特性(一)

格式化输入与输出

除了条件状态外,每个iostream对象还维护一个格式状态来控制IO如何格式化的细节,比如控制整数的进制、浮点数的精度、输出宽度等。
标准库定义了一组操纵符来改变流的格式状态,一个常用的操纵符:endl,一般将它“写”到输出流,就像它是一个值一样,但它不是一个普通值,而是一个操作:它输出一个换行符并刷新缓冲区。

很多操纵符改变格式状态
操作符用于两大类输出控制:控制数值的输出形式以及控制补白的数量和位置
大多数改变格式状态的操纵符都是设置/复原成对的:一个操纵符用来将格式状态设置为一个新值,另一个操纵符将其复原。
当操纵符改变流的格式状态后,通常改变后的状态对所有后续的IO都生效。

控制布尔值的格式
默认情况下,bool值打印为1或0,一个true值输出为整数1,一个false值输出为整数0。可以通过对流使用boolalpha操纵符来覆盖这种模式:

cout<<<<true<<" "<<false
    <<"\n"<<boolalpha<<true<<" "<<false<<endl;
//执行以上代码后的输出如下:
1 0
true false

一旦向cout“写入” boolalpha,就改变了cout打印bool值的方式,后续的布尔值打印操作都会打印true和false而不是1和0.
为了取消cout格式的改变,可以使用noboolalpha:

cout<<boolalpha<< true <<" "<<noboolalpha<<false <<endl;

//执行以上代码将会有如下输出:
true 0

指定整型值的进制
默认情况下,输出整型是十进制,可以通过操纵符hex、oct和dec将其改为十六进制、八进制或者改回十进制:

    cout<<"default:"<<15<<endl;
    cout<<"hex:"<<hex<<15<<endl;
    cout<<"oct:"<<oct<<15<<endl;
    cout<<"dec:"<<dec<<15<<endl;
//输出如下:
default15
hex:f
oct:17
dec:15

在输出中指出进制
默认情况下,打印出来的数值并看不出使用的是几进制,当使用操纵符showbase时,会在输出结果中显示进制:
前导0x表示十六进制
前导0表示八进制
没有前导表示十进制。

    cout<<"default:"<<showbase<<15<<endl;
    cout<<"hex:"<<hex<<15<<endl;
    cout<<"oct:"<<oct<<15<<endl;
    cout<<"dec:"<<dec<<15<<endl;
    cout<<noshowbase<<endl;    //恢复流状态
//输出将如下:
default15
hex:0xf
oct:017
dec:15

控制浮点数格式
可以控制浮点数输出三种格式:
1.以多高精度打印浮点数;
2.数值是打印为十六进制、十进制还是科学技术法形式;
3.对于没有小数部分的浮点值是否打印小数点。
默认情况下,浮点值按照六位数字精度打印;如果浮点值没有小数部分,则不打印小数点;会根据浮点数的值选择打印为十进制或是科学计数法形式(非常大或非常小的值会打印为科学计数法形式,其他值打印为定点十进制形式)。

指定打印精度
默认情况下,精度控制打印的数字的总数,当打印时,浮点数按当前精度舍入。可以通过调用IO对象的precision成员或setprecision操纵符来改变精度:

    cout<<"precision:"<<cout.precision()<<",value:"<<sqrt(2.0)<<endl;
    cout.precision(12);
    cout<<"precision:"<<cout.precision()<<",value:"<<sqrt(2.0)<<endl;
    cout<<setprecision(3);
    cout<<"precision:"<<cout.precision()<<",value:"<<sqrt(2.0)<<endl;

//输出如下:
precision:6,value:1.41421
precision:12,value:1.41421356237
precision:3,value:1.41

其他操作符
这里写图片描述
这里写图片描述

这里写图片描述


未格式化的输入输出

标准库提供了一组底层操作,来支持未格式化IO。这些操作允许将一个流当做无解释的字节序列来处理。

单字节操作
有几个未格式化操作每次操作一个字节地处理流,这些操作会读取空格而不是忽略。例如使用未格式化操作get和put来读取和写入一个字符:

char ch;
while(cin.get(ch)){
    cout.put(ch);
}

以上程序的输入输出完全相同
这里写图片描述

将字符放回输入流
有时需要读取一个字符才知道还未准备好处理它,这时需要将字符放回流中,标准库中有三种退回字符的方法:
1.peek:返回输入流中的下一个字符的副本,但不会将它从流中删除,peek返回的值仍然留在流中;
2.unget:使得输入流向后移动,从而最后读取的值又回到流中,即使不知道最后从流中读取什么值,仍然可以调用unget;
3.putback:一个更为特殊的unget,他退回从流中读取的最后一个值,但它接受一个参数,此参数必须与最后读取的值相同。

从输入操作返回的是int值
函数peek和无参的unget版本都以int类型从输入流中返回一个字符,这里是返回int而不是返回char的原因是:返回的int值还可以返回文件尾标记,而char范围的每个值都只能表示一个真实的字符,没有额外的值来表示文件尾。
返回int的函数将它们要返回的字符先转换成unsigned char,然后再将结果提升到int。因此,即使字符集中有字符映射到负数,这些操作返回的值也是正值,而标准库使用负值来表示文件尾。头文件cstdio中定义了一个名为EOF的const,可用来检测是否到了文件尾:

//这个ch是定义的int类型,这很重要
int ch;
while((ch = cin.get()) != EOF){
    cout.put(ch);
}

多字节操作
一些未格式化IO操作一次处理大块数据。这些操作除了容易出错外,还要求程序员来分配并管理用来保存和提取数据的字符数组。
这里写图片描述
这里写图片描述

get和getline两个函数的行为类似但不完全相同:两个函数的差别是处理分隔符的方式:get将分隔符留作istream中的下一个字符,而getline则读取并丢弃分隔符,无论哪个函数都不会将分隔符保存在用来保存数据的数组中。

确定读取了多少个字符
某些操作从输入读取未知个数的字节,可以使用gcount来确定最后一个未格式化操作从输入读取了多少个字符。应该在任何后续未格式化输入操作之前调用gcount。特别是,将字符退回流中的操作也是未格式化输入操作,如果使用了peek、unget或putback后调用gcount,则gcount的返回值将为0。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章