缓冲

从磁盘中每读取一个字符都需要大量的硬件活动,这样,向磁盘读写数据时就会花费很多时间,为了避免频繁的读写数据,流提供了缓冲技术。数据经过流的操作被读入缓冲区中,此时,该数据不会立即写到磁盘中,而是不断地将其他数据读入缓冲区中,知道缓冲区充满时,才一次性写入磁盘中。

缓冲区是内存中的一块区域,他对数据的处理速度比硬盘快,因此,当大量信息存储在缓冲区时,会无形中提高了程序对数据的访问速度。同时当数据填满缓冲区的时候,数据会一次性的写入硬盘,接着刷新缓冲区,为下次将数据读入缓冲区做准备。

在输出时,遇到结束符(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

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值