一、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、条件状态
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 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流
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;
}