C++中的IO库(二)

时间:2014.03.17

地点:基地二楼

-------------------------------------------------------------------

一、管理输出缓冲

  每个输出流都会管理一个输出缓冲区,用来保存程序读写的数据。比如:

cout<<Please input a number<<endl;
这样文本串可能被立即打印出来,也可能先被操作系统保存到缓冲区,然后再打印。设备的写操作可能是费时的,这样,操作系统就可以将程序的多个输出操作组合成单一的系统级写操作或者单一的设备写操作可带来性能上的很大提升。

  下面总结了导致缓冲刷新的原因:

1.程序正常结束,作为main函数的return操作的一部分,缓冲刷新被执行。

2.缓冲区满时需要刷新缓冲区之后新的数据才能被继续写入。

3.操纵符endl可以显示刷新缓冲区

4.在每个输出操作之后,可用操纵符unitbuf设置流的内部状态来清空缓冲区。默认情况下对cerr是设置unitbuf的,因此写到cerr的内容都是立即刷新的。

5.一个输出流可能被关联到另一个流。此时,当读写被关联的流时,关联到的流的缓冲区会被刷新。比如,默认情况下cin和cerr都关联到cout,因此,读cin或写cerr都会导致cout的缓冲区被刷新。

-------------------------------------------------------------------

二、刷新输出缓冲区

  使用操纵符endl可以刷新缓冲区并完成换行工作。类似的flush也能刷新缓冲区,但不输出任何额外字符,不像endl那样还会换行。ends则是向缓冲区插入一个空字符,然后刷新缓冲区。他们的用法和endl一样。

如果想在每次输出操作后都刷新缓冲区,可以使用unitbuf操纵符来设置流的内部状态清空缓冲区。它告诉流在接下来的每次输出写操作之后都进行一次flush操作,而nounitbuf操纵符则重置流,使其恢复使用正常的系统管理的缓冲区刷新机制:

cout<<unitbuf;  //现在所有输出操作后都会立即刷新缓冲区
cout<<nounitbuf;  //恢复正常的缓冲方式

注:如果程序崩溃,输出缓冲区不会被刷新,所有输出数据很可能停留在输出缓冲区等待打印。

另外,当输入流被关联到输出流,那么一旦从输入流读取数据,都会先刷新关联的输出流。标准库将cout和cin关联在一起。

所以当执行:

cin>>ival;
时,将导致cout的缓冲区被刷新。这在交互式系统通常应该关联输入流和输出流。即所有输出和用户提示信息都会在读操作之前被打印出来。说道关联输入和输出流,就要说道tie函数,它有两个版本,一个不带参数,返回指向输出流的指针,如果本对象当前关联到一个输出流,则返回这个输出流的指针,如过对象没有关联到流返回空指针。tie还一个版本接受一个指向ostream的指针,将自己关联到次ostream。比如 x.tie(&o)即将流x关联到输出流o 。(&o  这里是对输出流对象o取址)。我们可以将一个istream对象关联到一个ostream对象也可以将一个ostream对象关联到另一个ostream对象。
cin.tie(&cout);  //标准库中的将cin和cout关联在一起

如果想解开流的关联,可以将对象tie到一个空指针上,比如:

ostearm *old_tie=cin.tie(nullptr);  //cin不再与其他流关联

-------------------------------------------------------------------

三、文件的输入输出

a.IO

  头文件fstrem中定义了三个支持文件IO

1. ifstream 从给定文件读取数据

2. ofstream 向给定文件写入数据

3. fstream 读写给定文件

fstream继承自iostream,所以诸如IO运算符<<  >> cin cout getline等都能适用,另外它还有些新成员来管理与流关联的文件。当然这些时fstream中特有的,可以对fstream ifstream ofstream对象调用。总结如下

1. fstream  fstrm;       创建一个未绑定的流,fstream是头文件fstream中定义的一个类型

2. fstream fstrm(s);   c创建一个fstream,并打开名为s的文件,s可是string类型或是一个指向C风格的字符串指针,这些构造函数是explict的

3.fstream fstrm(s,mode)  和以上一样,按指定的mode打开文件


fstrm.open(s)    打开名为s的文件,将文件与fstrm绑定,返回void

fstrm.close()    关闭与fstrm绑定的文件,返回void

fstrm.is_open()  返回一个bool值,指出绑定文件是否打开

 b.使用文件流对象

  当有需要读写一个文件时,我们可先定义一个文件流对象,并将对象与文件关联绑定。创建文件流对象时,我们可提供文件名(选用),如果提供了文件名open成员函数会被自动调用,比如:

ifstream in(ifile);  //构造一个ifstream并打开给定文件
ofstream out;    //这是一个输出文件流但还未关联到任何文件

我们已经知道,通常我们可以将一个派生类对象当做基类对象使用,因此,在需要iostream类型引用或指针的地方,我们也可用对应的fstream来调用。例如,假定输入输出文件名是通过传递给main函数的参数来指定,可以这样写:

ifstream input(argv[1]);  //打开文件,建立一个文件输入流对象input
ofstream output(argv[2]);   //打开输出文件,建立一个文件输出流对象output
再举一例:

ifstream in(ifile);  //构建一个ifstream并打开给定文件
ofstream out;   //构建了一个输出文件流,但没有与任何文件绑定
out.open(ifile+".copy");  将流对象绑定指定文件并打开

像这里要是open失败了,failbit会被置位,调用open是有可能失败的,因为调用open时对它进行检测通常是一个好习惯,它和cin用作条件类似,open要是失败,条件会为假,就不会去使用out流了。比如:

if(out)   //检查open是否成功,成功了,就可以使用文件了
一旦文件流打开,它就保持了与对应文件的关联,对一个已打开的文件流调用open会失败,并导致failbit被置位。随后任何试图使用文件流的操作都会失败。这和socket 、serial等一样啊。如果你想改变文件流关联的对象,那么必须先关闭当前已关联的文件,关闭成功后再去关联或者打开新的文件,比如:

in.close();           //关闭当前关联的文件
in.open(ifile+"2");  打开另一个文件
一旦open成功,则open会设置流的状态,使得good()为true

c.自动构造和析构

 任务:由main函数接受要处理的一个文件列表,考虑用循环的方式解决问题,实现如下:

for(quto p=argv+1:p!=argv+argc;++p){
  ifstream input(*p);  //创建文件输出流并打开文件
  if(input){         //如果创建成功,处理文件
      process(input);
  }else
      cerr<<"couldn't open: "<<string(*p);
}  //每次循环后创建的input流对象会离开作用域,自动销毁
上面流对象在每次循环中都将被重新创建和销毁,当一个流对象离开其作用域时,与之关联的文件会自动关闭,即close会被自动调用。
-------------------------------------------------------------------

四、文件模式

  文件被open时会涉及文件模式,它指出如何来使用文件,总结如下;

1.  in  以读的方式打开文件
2.  out  以写得方式打开文件

3.  app  每次写操作前均定为到文件末尾

4.  ate  打开文件后立即定为到文件末尾

5.  trunc  截断文件

6.  binary  以二进制方式进行IO
相关原则如下:

1.  只能对ofstream或fstream对象设定out模式

2. 只能对ifstrem或fstream对象设定in模式

3. 只有当out也被设定时才能设定trunc模式

4. 只要trunc没被设定,就可以设定app模式,在app模式下,即使没有显示指定out模式,文件也总是以输出方式被打开

5.默认情况,即使没有指定trunc,以out模式打开的文件也会被截断,为了保留以out模式打开的文件的内容,必须同时指定app模式,这样只会将数据追加写到文件末尾,或者同时指定in模式,即打开文件同时进行读写操作

6.每个文件流类型都定义了一个默认的文件模式,总结如下:

       ifstream——in模式                         ofstream——out模式

       fstream——in 和 out模式

需要说明的是以out模式打开文件会丢弃已有的数据,默认情况下,打开一个ofstream时文件的内容会被丢弃,然而阻止这一清空给定文件的好办法就是打开的同时指定app模式。

//以下语句,file会被截断
ofstream out("file1");  //隐含以输出模式打开文件并截断文件
ofstream out2("file1",ofstream::out); //隐含地截断文件
ofstream out3("file1",ofstream::out | ofstream::trunc)

//为了保留文件内容,我们必须显示指定app模式
ofstream out("file",ofstream::app);  //隐含为输出模式
ofstream out2("file",ofstream::out | ofstream::app);
总结:要想保留被ofstream打开的文件中已有数据的唯一方法就是显示的指定文件模式为in或app模式。

每次调用open时都会确定文件模式,对于一个给定的流,每次打开文件时都可以改变其文件模式,例如:

ofstream out;   //未指定文件打开模式
out.open("file");  //默认文件out隐含截断
out.close();
out.open("file1",ofstream::app);  //模式为输出和追加
out.close();
通常情况下,out模式即意味着同时使用trunc模式,文件内容将被清空,打开file1文件时,指定了append模式,文件中已有的数据将会得到保留,所有写操作都会在文件末尾进行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值