1. IO类
IO类和IO对象操作的实际上是char数据(字符数据流)。默认情况下IO对象是与用户控制台窗口绑定,即从控制台窗口读数据或写数据。实际上,char数据的来源还可以来自文件,或一个string。此外,C++还支持宽字符(wide-character)的读写。
Header | Type |
iostream | istream, wistream reads from a stream ostream, wostream writes to a stream iostream, wiostream reads and writes a stream |
fstream | ifstream, wifstream reads from a file ofstream, wofstream writes to a file fstream, wfstream reads and writes a file |
sstream | istringstream, wistringstream reads from a string ostringstream, wostringstream writes to a string stringstream, wstringstream reads and writes a string |
1.1 IO对象不能赋值或拷贝
所以与IO相关的函数只能传递和返回引用。因为读或写会改变IO对象的状态,所以,IO对象的引用不能为const。
1.2 条件状态
IO类定义了一组函数和标记,可以用来访问和操作数据流的条件状态。
strm::iostate | strm是表一中的一个IO类型。iostate是与机器有关的整型, 代表数据流的状态 |
strm::badbit | strm::iostate值,用来指示数据流已损坏 |
strm::fail | strm::iostate值,用来指示IO操作失败 |
strm::eofbit | strm::iostate值,用来指示数据流碰到了结束符end-of-file |
strm::goodbit | strm::iostate值,用来指示数据流不在错误状态。该值确保为0 |
s.eof() | 如果数据流s的eofbit设置了,则为真 |
s.fail() | 如果数据流s的failbit 或 badbit设置了,则为真 |
s.bad() | 如果数据流s的badbit设置了,则为真 |
s.good() | 如果数据流s在合法状态,则为真 |
s.clear() | 重置数据流s的所以条件值为合法状态。返回void。 |
s.clear(flags) | 将s的条件置为flags。flags的类型是strm::iostate。返回void |
s.setstate(flags) | 增加一个指定条件到s。flags的类似是strm::iostate。返回void |
s.rdstate() | 返回s的当前条件,返回类型为strm::iostate |
int a;
cin >> a;
如果从键盘输入data,则会读失败,因为a是一个整型。此时,failbit会设置。
iostate类型是用做位模式。通过位操作来测试或设置标记位。
badbit指示一个系统级的失败,例如
无法恢复的读或写错误。此时流一般无法再使用。
failbit指示一个
可恢复的错误,例如需要数值数据时读入字符。该错误一般可以纠正并继续使用流。
遇到文件结束时会设置
eofbit和
failbit。
goodbit保证为0值,指示了流没有错误。
rdstate函数返回当前流的状态值。
setstate设置一个状态标记,以指示问题的出现。
clear有两个版本:不带参数的版本关闭所有的失败位,即clear()之后调用good会返回真。带一个参数的版本用来设置新的状态位。比如关闭failbit和badbit的操作:
cin.clear(cin.rdstate() & ~cin.failbit & ~cin.badbit);
1.3 输出缓冲管理
每个输出流都会管理一个缓冲区,用来保存程序需要读和写的数据。当执行一条输出指令时,输出的字符串文本可能直接打印,也可能由操作系统保存到缓冲区以供随后打印。
使用缓冲区可以使操作系统将多个输出操作合并成一个系统级的写操作。因为往设备中写是很花费时间的,这种合并可以大大提高效率。会导致缓冲区刷新(写入输出设备或文件)的条件有:
使用缓冲区可以使操作系统将多个输出操作合并成一个系统级的写操作。因为往设备中写是很花费时间的,这种合并可以大大提高效率。会导致缓冲区刷新(写入输出设备或文件)的条件有:
- 程序正常结束。main函数return时,所以的输出缓冲区都会刷新。
- 在某个不确定的时间,当缓冲区满的时候,会自动刷新。
- 我们可以使用endl手动刷新缓冲区。
- 可以使用unitbuf操作设置流的内部状态使得每次输出操作后都使得流为空。cerr在每次写操作后都会直接刷新。
- 一个输出流可以绑定到另一个流。此时,当被绑定的流读或写时,该输出流会刷新。默认时,cin和cerr都被cout绑定。因此,读cin或写cerr会刷新cout。
刷新控制符
刷新输出缓冲的控制符,除了endl,还有flush和ends.
cout << "hello" << endl; // 写入 hello 和一个换行,然后刷新缓冲
cout << "hello" << flush; // 写入 hello,然后刷新缓冲,不添加数据
cout << "hello" << ends; // 写入hello 和一个null字符,然后刷新缓冲
unitbuf控制符使得每次写操作后都刷新缓冲。nounitbuf控制符用来恢复到正常的,系统管理的刷新。
cout << unitbuf;
cout << nounitbuf;
输入与输出缓冲绑定
当一个输入流被绑定到一个输出流时,任何从输入流的读操作之前都会先刷新与其绑定的输出流。cout就是与cin绑定,所以 cin >> val;在读之前会先刷新cout。【注】交互式的系统一般都需要将输入流与输出流绑定。这样在用户输入前,包含提示信息的输出流就会先刷新,以提示用户的操作。
有两个版本的tie:不带参数的版本会返回与其绑定的流,如果没有绑定,则返回null;带参数的版本用来将一个输入或输出流绑定到一个输出流。
cin.tie(&cout); // tie cin to cout
ostream *old_tie = cin.tie(null); // untie
每个流只能绑定到最大一个流。而多个流可以绑定到同一个流。
2. 文件输入和输出
ifstream继承于istream,ofstream继承于ostream。我们可以使用IO操作(<<和>>)来读和写文件,我们可以使用getline来读ifstream。
fstream fstrm; | 创建一个没有绑定的文件流。fstream是定义在fstream头文件中的类型。 |
fstream fstrm(s); | 创建一个fstream,并打开一个名为s的文件。s可以是string类型, 或是指向C-风格字符串的指针。这些构造函数是explicit的。默认 文件模式取决于fstream的类型。 |
fstream fstrm(s, mode); | 用指定的模式打开文件s |
fstrm.open(s); | 打开名为s的文件,并将其绑定到fstrm |
fstrm.open(s, mode); | 用给定模式打开文件s |
fstrm.close(); | 关闭fstrm绑定的文件。返回void |
fstrm.is_open(); | 返回bool类型,来指示与fstrm绑定的文件是否成功打开且还没有关闭。 |
2.1 使用文件流对象
需要指出的是,新标准允许用string类型表示的文件名来打开文件,而C++98则只允许使用C-风格字符串。
如果打开文件失败,则failbit会设置。所以最好检查文件打开是否成功:
string filename
ifstream fin(filename);
if (fin) {
// do file read
}
因为一个流一次只能与一个文件绑定,多次绑定会导致失败,并设置failbit。而后续的文件操作都会失败。所以用同一个流打开另一个文件之前,确保上一个文件已经关闭。
ifstream fin(filename);
fin.close();
fin.open(filename1);
【注】绑定于流的文件在其作用范围结束时会自动关闭。即:在流对象销毁时,其绑定的文件也会自动关闭。
2.2 文件模式
in | 读 |
out | 写 |
app | 在尾部追加 |
ate | 文件打开后,立即定位到文件尾 |
trunc | 文件截断 |
binary | 用二进制模式进行IO操作 |
- out只能用于ofstream或fstream对象
- in只能用于ifstream或fstream对象
- trunc只能在指定out时设置
- app模式不能和trunc一起使用,如果指定了app,则文件总是以output模式打开,不需要显式指定
- out模式打开的文件是默认截断的,即原有的内容会被删除;为了防止该情况发生,要么指定app,使得在文件尾追加内容,要么指定in,即文件可以同时读和写。
每个文件流类型都定义了默认文件模式。ifstream默认模式为in;ofstream默认模式为out;fstream的默认模式为in和out。
//文件被截断
ofstream out("file1"); // out and trunc are implicit
ofstream out2("file1", ofstream::out); // trunc is implicit
ofstream out3("file1", ofstream::out | ofstream::trunc);
//文件内容保留
ofstream app("file2", ofstream::app); // out is implicit
ofstream app("file2", ofstream::out | ofstream::app);
3. string流
sstream strm; | strm是一个未绑定的stringstream.sstream是sstream头文件 中定义的类型 |
sstream strm(s); | strm是一个由串s初始化的sstream。该构造函数是explicit的 |
strm.str() | 返回strm保存的串 |
strm.str(s) | 将串s复制到strm,返回void |
3.1 使用istringstream
istringstream一般用来处理每行包含的数据模式不同的情况。比如:
98 10 20
20 30
15 40 38 50 18
每行包含的数据个数不同,我们需要计算每行整数的和:
string line;
while (getline(cin, line)) {
int data;
int sum = 0;
istringstream record(line);
while (record >> data) {
sum += data;
}
cout << sum << endl;
}
3.2 使用ostringstream
我们可以使用ostringstream先将输出组织在一个string中,然后再统一输出。