一、IO对象不可复制或赋值
出于某些原因,标准库类型不允许做复制或赋值操作。比如,
ofstream out1,out2;
out1 = out2; //error:cannot assign stream objects
//print function: parameter is copied
ofstream = print(ofstream);
out2 = print(out2); //error:cannot copy stream objects
1)只有支持复制的元素类型可以存储在vector或其他容器类型里。由于流对象不能复制,因此不能存储在vector(或其他)容器中(即不存在存储流对象的vector或其他容器)。
2)形参或返回类型也不能为流类型。如果需要传递或返回IO对象,则必须传递或返回指向该对象的指针或引用:
ofstream &print(ofstream&); //ok:takes a reference, no copy
while (print(out2) ) {/*...*/} //ok:pass reference to out2
一般情况下,如果要传递IO对象以便对它进行读写,可用非const引用的方式传递这个流对象。对IO对象的读写会改变它的状态,因此引用必须是非const的。
二、流的条件状态
IO标准库管理一系列条件状态(condition state)成员,用来标记给定的IO对象是否处于可用状态。
- s.eof() end-of-file,文件结束符
- s.good() 如果流s处于有效状态,则该函数返回true
- s.clear() 将流s中的所有状态值都设为有效状态
- s.clear(flag) 将流s中的某个指定条件状态设置为有效。flag的类型是strm::iostate
- s.setstate(flag) 给流s添加指定条件。
- s.rdstate() 返回流s的当前条件
clear和setstate操作用于改变条件成员的状态。clear操作将条件重设为有效状态。在流的使用出现了问题并做出补救后,如果我们希望把流重设为有效状态,则可以调用clear操作。使用setstate操作可打开某个指定的条件,用于表示某个问题的发生。
流的状态邮bad、fail、eof和good操作揭示。如果bad、fail或eof中的任一个为true,流本身将处于错误状态。
1.条件状态的访问
rdstate成员函数返回一个iostate类型的值,该值对应于流当前的整个条件状态:
//remenber current state of cin
istream::iostate old_state = cin.rdstate();
cin.clear();
process_input(); //use cin
cin.clear(old_state); //now reset cin to old state
三、输出缓冲区的管理
每个IO对象管理一个缓冲区,用于存储程序读写的数据。
os<<"please enter a value:";
系统将字符串字面值存储在与流os关联的缓冲区中。下面几种情况将导致缓冲区的内容被刷新:
1)程序正常结束。作为main返回工作的一部分,将清空所有输出缓冲区。
2)在一些不确定的时候,缓冲区可能已经满了,在这种情况下,缓冲区将会在写下一个值之前被刷新。
3)用操作符显式的刷新缓冲区,例如行结束符endl。
4)在每次输出操作执行完后,用unitbuf操纵符设置流的内部状态,从而清空缓冲区。
5)可将输出流与输入流关联(tie)起来。在这种情况下,在读输入流时将刷新其关联的输出缓冲区。
1.输出缓冲区的刷新(flush、ends、endl)
cout<<"hi"<<flush; //flushes the buffer;adds no data
cout<<"hi"<<ends; //inserts a null,then flushes the buffer
cout<<"hi"<<endl; //inserts a newline,then flushes the buffer
2.unitbuf操作符
如果需要刷新所有输出,最好使用unitbuf操作符。这个操作符在每次执行完写操作后都刷新流:
cout<<unitbuf<<"first"<<"second"<<nounitbuf;
等价于:
cout<<"first"<<flush<<"second"<<flush;
nounitbuf操作符将恢复为使用正常的、由系统管理的缓冲区刷新方式。
note:程序崩溃后不会刷新缓冲区
四、文件的输入和输出
C++中的文件名:
由于历史原因,IO标准库使用C风格字符串而不是C++ string类型的字符串作为文件名。在创建fstream对象时,如果调用open或使用文件名作初始化式,需要传递的实参应为C风格字符串,而不是标准库string对象。程序常常从标准输入获得文件名。通常,比较好的方法是将文件名读入string对象,而不是C风格字符数组。假设要使用的文件名保存在string对象中,则可调用c_str成员获取C风格字符串。
1.文件流对象的使用
需要读写文件时,则必须定义自己的对象,并将它们绑定在需要的文件上。假设ifile和ofile是存储希望读写的文件名的string对象:
//construct an ifstream and bind it to the file named ifile
ifstream infile(ifile.c_str());
//ofstream output file object to write file named ofile
ofstream outfile(ofile.c_str());
infile和ofile分别是读文件的流对象和写文件的流对象
以上是在定义的时候直接初始化打开一对fstream对象。若开始未初始化,如下:
ifstream infile; //unbound input file stream
ofstream outfile; //unbound output file stream
上面定义的两对象均未绑定具体文件。在使用fstream对象之前,还必须使这些对象捆绑要读写的文件:
infile.open("in"); //open file named "in" in the current directory
outfile.open("out"); //open file named "out" in the current directory
调用open成员函数将已存在的fstream对象与特定文件绑定。
将文件与新文件流重新绑定
ifstream infile("in");
infile.close();
infile.open("next");
在尝试打开新文件之前先关闭当前的文件流
五.字符串流
标准库string类型
#include <string>
using std::string;
string对象的定义和初始化
- string s1; 默认构造函数,s1为空串
- string s2(s1); 将s2初始化为s1的一个副本
- string s3("value"); 将s3初始化为一个字符串字面值副本
- string s4(n,'c'); 将s4初始化为字符'c'的n个副本
Note:因为历史原因以及为了与C语言兼容,标准库string类型和字符型字面值不是同一种类型
string对象的读写
cin>>s;
从标准输入读取string,并将读入的串存储在s中。string类型的输入操作符:
- 读取并忽略开头所有的空白字符(如空格,换行符,制表符)。
- 读取字符直至再次遇到空白字符,读取终止。
因此,如果输入到程序的是“ hello world! ”,屏幕上将输出“hello”,而不含任何空格。
1.stringstream对象的使用
string line, word; //will hold a line and word from input,respectively
while(getline(cin, line)) { //read a line from the input into line
//do per-line processing
istringstream stream(line); //bind to stream to the line we read
while(stream>>word) { //read a word from line
//do per-word processing
}
}
这里使用getline函数从输入读取整行内容。然后为了获得每行中的单词,将一个istringstream对象与所读取的行绑定起来,这样只需使用普通的string输入操作符即可读出每行中的单词。
2.stringstream提供的转换和/或格式化
int val1 = 512, val2 = 1024;
ostringstream format_message;
//ok:converts values to a string representation
format_message<<"val1: "<<val1<<"\n"
<<"val2: "<<val2<<"\n";
format_message里面的内容是以下字符:
val1: 512\nval2: 1024
用istringstream读string对象
//str member obtains the string associated with a stringstream
istringstream input_istring(format_message.str());
string dump; //place to dump the labels from the formatted message
//extracts stored ascii values,converting back to arithmetic types
input_istring>>dump>>val1>>dump>>val2;
cout<<val1<<" "<<val2<<endl; //print 512 1024
这里使用str成员获取与之前创建的ostringstream对象关联的string副本,再将input_istring与string绑定起来。
C++使用标准库类处理输入和输出:
iostream类处理面向流的输入和输出。
fstream类处理已命名文件的IO。
stringstream类处理内存中字符串的IO。