C++ primer 学习笔记——第八章 IO库

一、IO类

IO库类型和头文件
头文件 类型
iostream(流) istream,wistream从流读取数据
ostream,wostream向流写入数据
iostream,wiostream读写流
fstream(文件) ifstream,wifstream从文件读取数据
ofstream,wofstream向文件写入数据
fstream,wfstream读写文件
sstream(string) istringstream,wistringstream从string读取数据
ostringstream,wostringstream向string写入数据
stringstream,wstringstream读写string

IO类型间的关系

标准库使我们能忽略这些不同类型的流之间的差异,这是通过继承机制。

类型ifstream和istringstream都继承自istream;类型ofstream和ostringstream都继承自ostream。

本节剩下部分所介绍的标准库流特性都可以无差别地应用于普通流、文件流和string流,以及char或宽字符流版本。

1、IO对象无拷贝或赋值

由于不能拷贝IO对象,因此我们也不能将形参或返回类型设置为流类型

进行IO操作的函数通常以引用方式传递和返回流。

读写一个IO对象会改变其状态,因此传递和返回的引用不能是const的。

2、条件状态

IO库条件状态
strm::iostate  
strm::badbit  
strm::failbit  
strm::eofbit  
strm::goodbit  
s.eof()  
s.fail()  
s.bad()  
s.clear()  
s.clear(flags)  
s.setstate(flags)  
s.rdstate()  

确定一个流对象的状态的最简单的方法是将它当做一个条件来使用,如果输入操作成功,流保持有效状态,则条件为真:

while(cin>>word)
    //OK::读操作成功

查询流的状态

管理流的状态

以上两种行为涉及到位的运算

3、管理输出缓冲

刷新输出缓冲区

操纵符endl,完成换行并刷新缓冲区

flush刷新缓冲区,但是不输出任何额外的字符

ends向缓冲区插入一个空字符,然后刷新缓冲区

cout<<"hi!"<<endl;    //输出hi和一个换行,然后刷新缓冲区
cout<<"hi!"<<flush;   //输出hi,然后刷新缓冲区,不附加任何额外字符
cout<<"hi!"<<ends;    //输出hi和一个空字符,然后刷新缓冲区

unitbuf操纵符

cout<<unitbuf;      //所有输出操作后都会立即刷新缓冲区
//任何输出都立刻刷新,无缓冲
cout<<nounitbuf;    //回到正常的缓冲方式

警告:如果程序崩溃,输出缓冲区是不会被刷新的。当一个程序崩溃后,它输出的数据很可能停留在输出缓冲区中等待打印

关联输入和输出流

当一个输入流被关联到一个输出流时,任何试图从输入读取数据的操作都会先刷新关联的输出流。

tie有两个重载的版本:一个版本不带参数,返回指向输出流的指针。如果关联到一个输出流,则返回指向这个流的指针,如果未关联到流,则返回空指针。tie的第二个版本接受一个指向ostream的指针,将自己关联到此ostream。即,x.tie(&o)将x关联到输出流o。

cin.tie(&cout);   //将cin与cout关联在一起
//old_tie指向当前关联到cin的流(如果有的话)
ostream &old_tie=cin.tie(nullptr);   //cin不再与其他流关联
cin.tie(&cerr);    //读取cin会刷新cerr而不是cout
cin.tie(old_tie);  //重建cin和cout的正常关联

每个流同时最多关联到一个流,但多个流可以同时关联到同一个ostream。

 

二、文件输入输出

头文件fstream定义了三个类型类支持文件IO:ifstream从一个给定文件读取数据,ofstream向一个给定文件写入数据,以及fstream可以读写给定文件

fstream特有的操作
fstream fstrm 创建一个未绑定的文件流
fstream fstrm(s); 创建一个fstream,并打开名为s的文件。s可以是string类型,或者是一个指向C风格字符串的指针
fstream fstrm(s,mode); 与前一个构造函数类似,但按指定mode打开文件
fstrm.open(s); 打开名为s的文件,并将文件与fstrm绑定
fstrm.close() 关闭与fstrm绑定的文件,返回void
fstrm.is_open(); 返回一个bool的值,指出与fstrm关联的文件是否成功打开且尚未关闭

1、使用文件流对象

如果提供一个文件名,则open会自动被调用:

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

用fstream代替iostream&

在要求使用基类型对象的地方,我们可以用继承类型的对象来代替。所以,接受一个iostream类型引用(或指针)参数的函数,可以用一个对应的fstream(或sstream)类型来调用:

ifstream input(argv[1]);
ofstream output(argv[2]);
Sales_data total;
if(read(input,total)){
    Sales_data trans;
    while(read(input,trans)){
        if(total.isbn()==trans.isbn())
            total.combine(trans);
        else
        {
            print(output,total)<<endl;
            total=trans;     
        }
    }
    print(output,total)<<endl;
}else
    cerr<<"No data?!"<<endl;

成员函数open和close

ifstream in(ifile);      //构筑一个ifstream并打开给定文件
ofstream out;            //输出文件流未与任何文件关联
out.open(ifile+".copy"); //打开指定文件ifile.copy

if(out) //检查open是否成功
        //open成功,我们就可以使用文件了

一旦一个文件流已经打开,它就保持与对应文件的关联。为了将文件流关联到另外一个文件,必须首先关闭已经关联的文件:

in.close();          //关闭文件
in.open(ifile+"2");  //打开另外一个文件

自动构造和析构

当一个fstream对象被销毁时,close对象会自动被调用

2、文件模式

每个流都有一个关联的文件模式,用来指出如何使用文件:

文件模式
in 以读方式打开
out 以写方式打开
app 每次写操作均定位到文件末尾
ate 打开文件后立即定位到文件末尾
trunc 截断文件
binary 以二进制方式进行IO

以out模式打开文件会丢弃已有数据

默认情况下,当我们打开一个ofstream时,文件的内容会被丢弃。阻止一个ofstream清空给定文件内容的方法是同时指定app模式:

//在这几条语句中,file1都被截断
ofstream out("file1"); //隐含以输出模式打开文件并截断文件
ofstream out1("file1",ofstream::out);    //隐含地截断文件
ofstream out2("file1",ofstream::out|ofstream::trunc); 
//为了保留文件内容,我们必须显式指定app模式
ofstream app("file2",ofstream::app);    //隐含为输出模式
ofstream app2("file2",ofstream::out|ofstream::app);

保留被ofstream打开的文件中已有数据的唯一方法是显式指定app或in模式

每次调用open时都会确定文件模式

ofstream out;   //未指定文件打开模式
out.open("scratchpad");    //模式隐含设置为输出和截断
out.close();    //关闭out,以便我们将其用于其他文件
out.open("precious",ofstream::app);    //模式为输出和追加
out.close();

三、string流

stringstream特有的操作
sstream strm; strm是一个未绑定的stringstream对象
sstream strm(s); strm是一个sstream对象,保存string s的一个拷贝
strm.str(); 返回strm所保存的string的拷贝
strm.str(s); 将string s拷贝到strm中。返回void

1、使用stringstream

当我们的某些工作是对整行文本进行处理,而其他一些工作是处理行内的单个单词时,通常可以使用istringstream。

例如一个文件,保存了一些人和他们的电话号码:

morgan 762386482746   34832283742897

drew  293874284083  3948743985799

lee 2837485959  272829110395857   887626374858

struct PersonInfo{
    string name;
    vector<string> phones;
};

string line,word;
vector<PersonInfo> people;
while(getline(cin,line)){
    PersonInfo info;
    istringstream record(line);
    record>>info.name;
    while(record>>word)
        info.phones.push_back(word);
    people.push_back(info);
}

3、使用ostringstream

当我们逐步构造输出,希望最后一起打印时,ostringstream是很有用的:

for(const auto &entry:people){
    ostringstream formatted,badNums;
    for(const auto &nums:entry.phones){
        if(!valid(nums)){
            badNums<<" "<<nums;
        }else
          formatted<<" "<<format(nums);
    }
    if(badNums.str().empty())
        os<<entry.name<<" "
          <<formatted.str()<<endl;
    else
        cerr<<"input error"<<entry.name()
            <<" invalid number(s) "<<badNums.str()<<endl;    
}

猜你喜欢

转载自blog.csdn.net/oil_you/article/details/82804551