缓冲
从磁盘中每读取一个字符都需要大量的硬件活动,这样,向磁盘读写数据时就会花费很多时间,为了避免频繁的读写数据,流提供了缓冲技术。数据经过流的操作被读入缓冲区中,此时,该数据不会立即写到磁盘中,而是不断地将其他数据读入缓冲区中,知道缓冲区充满时,才一次性写入磁盘中。
缓冲区是内存中的一块区域,他对数据的处理速度比硬盘快,因此,当大量信息存储在缓冲区时,会无形中提高了程序对数据的访问速度。同时当数据填满缓冲区的时候,数据会一次性的写入硬盘,接着刷新缓冲区,为下次将数据读入缓冲区做准备。
在输出时,遇到结束符(endl)时才刷新输出缓冲区,而输入时,则是按Enter键时才会刷新输入缓冲区并且将数据写入磁盘。这就是为什么我们按下的Enter键时才会处理输入数据的原因。
流和缓冲区
- streambuf类管理缓冲区,它的成员函数提供了填充缓冲区、访问缓冲区、刷新缓冲区和管理缓冲区内存的功能。
- ios_base是ios的基类,该类提供了流的一般方法,如输入/输出操作,判断数据流是否可读,是二进制数还是文本。
- ios类是输入和输出流类的基类,他有一个指向streambuf对象的指针成员。
- istream类从ios类中派生,提供了输入方法。
- ostream类从ios类中派生,提供了输出方法。
- iostream类是从istream类和ostream类中派生的,因此它继承了输入和输出的方法。
- fstream类提供了对文本的输入和输出操作。
标准输入/输出对象
- iostream是从istream和ostream类中派生的,因此它分别继承了iostream类的cin对象和ostream类的cout对象。另外,还有cerr和clog对象。
- cerr对象用于向标准输出设备(屏幕)发送非缓冲错误信息,由于是非缓冲的,因此错误信息不会等到缓冲区满了才输出到屏幕,而是直接输出到屏幕。
- clog对象用于向标准输入输出设备(屏幕)发送缓冲错误信息,由于是缓冲信息,因此会等到缓冲区满后才输出到屏幕,而不是直接到屏幕。这种输出一般重定向到日志文件中。
重定向
重定向的意思是可以冲洗定向输入输出的设备,比如我们可以输出到屏幕上,也可以输出到磁盘文件中或打印机中。一般情况下,clog对象会将错误的细心重新定向到一个文件中。
按位左移运算符“<<”或按位右移运算符“>>”也可以叫重定向运算符,因为它们可以把输入或者输出重新定向到不同的设备中,比如说屏幕、键盘、存储器活打印机。
DOS系统提供了这种重新定向运算符,如“>”和“<”。重定向输出运算符'“>”经常在等待用户键入DOS命令时出现,如D:\>。因此与其说重定向是iostream库的函数,不如说是操作系统的函数。
对象代表流
当我们在程序中使用iostream类的cout对象来输出一串数据时,这个cout对象将在输出之前保存与输出有关的数据,比如说,浮点数的字符数以及浮点数的小数位,还有用来处理缓冲区的streambuf对象的地址。这样,语句:
cout<<"hello word";
通过streambuf对象将字符串"hello word"方知道缓冲区中。ostream类中operator<<函数调用cout对象将字符串对象“hello word”的字符成员按流的方式输出到屏幕上。
该语句中cout对象处理的流源是程序中的未命名字符串'hello word",而流目标则是屏幕。同时,cout对象通过streambuf对象将字符串“hello word”放置到缓冲区中,并对缓冲区进行管理。
注意:操作符<<的默认含义是按位左移运算符,表达式i<<2的意思是将i的二进制位数左移2个字节,ostream类中重新定义了按位左移运算符<<,作用是将其重载为输出。因此它有了新的名称,叫做插入运算符。
清理缓冲区
在输出时,遇到结束符endl才刷新缓冲区,因此使用结束符endl,会在按回车键行的同时清理缓冲区。endl可以做两件事,这是因为endl在执行换行时调用了cout的成员函数flush(),该方法会输出缓冲区中所有的数据并且刷新缓冲区。因此我们也可以直接使用该方法,如
cout<<"hello word"<<flush;
区别是flush不会换行。
有关输出的相关函数
ostream提供了各种类型的operator<<()函数外,还提供了put()和write函数。
put()函数用于输出但个字符,write()用于输出字符串。
put()函数原型如下:
ostream &put(char);
从该原型中可以看出,put()接收单个char型字符并返回一个ostream的引用,假如用ostream类的cout对象来调用此函数,那么返回的将是cout对象。
cout.put('h').put('e').put('l').put('l').put('\n');
用cout对象调用write()函数的程序代码如下
#include <iostream>
using namespace std;
int main()
{
char ch[]="hello world";
int length=strlen(ch);
cout.write(ch,length).put('\n').write(ch,length-6)<<endl;
return 0;
}
//输出
//hello world
//hello
设置输出的字段宽度
默认的字段宽度一般刚好能容纳输出的字符、数字或者字符串。不过我们也可以使用width()来改变默认的宽度设置。
#include <iostream>
using namespace std;
int main()
{
cout.width(20);
cout<<"help"<<endl;
cout<<"help\n";
cout.width(1);
cout<<"help"<<endl;
return 0;
}
//输出
// help
//help
//help
设置填充字段
#include <iostream>
using namespace std;
int main()
{
cout.width(20);
cout.fill('*'); //fill()方法填充字符
cout<<"help"<<endl;
cout.width(25);
cout<<"help"<<endl;
return 0;
}
//输出
//****************help
//********************help
设置浮点数的显示精度
#include <iostream>
using namespace std;
int main()
{
float pi=3.1415926f;
float p2=9876.622345f;
cout<<pi<<endl;
cout<<p2<<endl;
cout.precision(15); //设置浮点数的显示精度
cout<<pi<<endl;
cout<<p2<<endl;
cout.precision(3);
cout<<pi<<endl;
cout<<p2<<endl;
}
/*输出
3.14159
9876.32
3.14159250259399
9876.6330703125
3.14
9.88e+003
*/
输出末尾的0
默认输出会省略掉末尾的0,但是有的时候我们需要输出小数点后面的0,需要用setf()函数。该函数不是iostream类的成员函数,而是ios_base类的函数,它的作用是控制输出的格式。比如要输出结尾的0,语句:
cout.setf(ios_base::showpoint);
#include <iostream>
using namespace std;
int main()
{
float price=4.0f;
cout.precision(6);
cout.setf(ios_base::showpoint);//showpoint指示是否显示点数,true为显示,false为不显示
cout<<price<<endl;
}
/*输出
4.00000
*/
设置标志
由于ios类从ios_base类派生而出,而istream类和ostream类分别从ios类派生,iostream又是从istream类和ostream类中派生而出,因此iostream间接的继承了ios_base类的setf()函数。ios_base类的设置函数setf()用来设置几种输出格式,比如:
cout.setf(ios::left); //按指定的宽度向左对齐
cout.setf(ios::internal); //符号左对齐,值右对齐
cout.setf(ios::right) //按指定的宽度向右对齐
setf函数原型
setf函数有两种原型:
fmtflags setf(fmtflags);
fmtflags是一种标志类型,他接收一个标志类型的变量并返回一个标志类型的变量,这个标志类型fmtflags其实就是int型,直接传入整数不大能看懂,因此一般采用传递标志枚举常量,如ios::left,不过这样也有坏处,如果ios::left、ios::right和ios::internal先后设置了left、right和internal所对应的标志后,向左对齐、向右对齐向中间对其三个标志同时被打开,导致程序不知道该执行哪个标志,从而被迫执行默认状态下的标志,也就是三个标志都没开启的初始状态。
要解决这个问题,就需要用到第二个重载函数:
fmtflags setf(fmtflags,fmtflags);
该函数有两个参数,第1个参数指定要打开的标志位,第2个参数则指定要清除的标志位。
由于left、right和internal不能同时打开,因此干脆用一个常量来统一管理这3个标志,这个常量叫指示标志位。它的作用是恢复三个left、right和internal的初始化状态,这个枚举常量也是ios从ios_base继承来的,因此要用ios或者ios_base加作用域标识符::访问它如:
ios::adjustfield或ios_base::adjustfield
#include <iostream>
using namespace std;
int main()
{
const float number=-185;
cout.width(10);
cout.setf(ios::left);
cout<<number<<endl;
cout.width(10);
cout.setf(ios::right,ios::adjustfield);
cout<<number<<endl;
cout.width(10);
cout.setf(ios::internal,ios::adjustfield);
cout<<number<<endl;
return 0;
}
/*输出
-185
-185
- 185
*/
所有的15个标志以及3个指示标志
15个标志的含义对照表
格式常量 意义 |
ios::boolalpha 开启boolapha标志位,输入和输出使用bool值(即Ture或False) |
ios::showbase 开启showbase标志位,向十六进制数添加Ox,向8进制添加0 |
ios::showpoint 开启showpoint标志位,显示末尾的小数点 |
ios::uppercase 开启uppercase标志位,用大写字母表示十六进制,用科学计数法表示十进制 |
ios::showpos 开启showpos标志位,在正数前面加+ |
ios::dec 开启dec标志位,输出十进制 |
ios::otc 开启hex标志位,输出八进制 |
ios::hex 开启hex标志位,输出十六进制 |
ios::fixed 开启fixed标志位,用十进制表示法表示浮点数 |
ios::scientific 开启scientific标志位,用科学计数法表示浮点数 |
ios::skipws 开启skipws标志位,跳过输入流中的空白符 |
ios::unitbuf 开启unitbuf标识符,每次执行输出操作后均会清空缓冲区 |
ios::left 开启left标志位,左对齐 |
ios::right 开启right标志位,右对齐 |
ios::internal 开启internal标志位,符号或技术前缀左对齐,值右对齐 |
具体内容参照《零起点学通c++》p575