继承机制使我们可以声明一个特定的类继承自另一个类。我们通常可以将一个派生类(继承类)对象当作其基类(所继承的类)对象来使用。类型
ifstream
和istringstream
都继承自istream
。因此,我们可以像使用istream
对象一样来使用ifstream
和istringstream
对象。也就是说,我们是如何使用cin
的,就可以同样地使用这些类型的对象。例如,可以对一个ifstream
或istringstream
对象调用getline
,也可以使用>>
从一个ifstream
或istringstream
对象中读取数据。类似的,类型ofstream
和ostringstream
都继承自ostream
。因此,我们是如何使用cout
的,就可以同样地使用这些类型的对象。不能拷贝或对 IO 对象赋值。由于不能拷贝 IO 对象,因此我们也不能将形参或返回类型设置为流类型。进行 IO 操作的函数通常以引用方式传递和返回流。读写一个 IO 对象会改变其状态,因此传递和返回的引用不能是
const
的。一个流一旦发生错误,其上后续的 IO 操作都会失败。只有当一个流处于无错状态时,我们才可以从它读取数据,向它写入数据。由于流可能处于错误状态,因此代码通常应该在使用一个流之前检查它是否处于良好状态。确定一个流对象的状态的最简单的方法是将它当作一个条件来使用:
1
2while (cin >> word)
// ok:读操作成功.....操作
good
在所有错误位均未置位的情况下返回 true,而bad
、fail
和eof
则在对应错误位被置位时返回 true。此外,在badbit
被置位时,fail
也会返回 true。这意味着,使用good
或fail
是确定流的总体状态的正确方法。实际上,我们将流当作条件使用的代码就等价于!fail()
。每个输出流都管理一个缓冲区,用来保存程序读写的数据。导致缓冲刷新(即,数据真正写到输出设备或文件)的原因有很多:
- 程序正常结束,作为
main
函数的return
操作的一部分,缓冲刷新被执行。 - 缓冲区满时,需要刷新缓冲,而后新的数据才能继续写入缓冲区。
- 可以使用操纵符如
endl
来显式刷新缓冲区。 - 在每个输出操作之后,可以用操纵符
unitbuf
设置流的内部状态,来清空缓冲区。默认情况下,对cerr
是设置unitbuf
的,因此写到cerr
的内容都是立即刷新的。 - 一个输出流可能被关联到另一个流。在这种情况下,当读写被关联的流时,关联到的流的缓冲区会被刷新。例如,默认情况下,
cin
和cerr
都关联到cout
。因此,读cin
或写cerr
都会导致cout
的缓冲区被刷新。
- 程序正常结束,作为
操纵符
endl
,它完成换行并刷新缓冲区的工作。IO 库中还有两个类似的操纵符:flush
和ends
。flush
刷新缓冲区,但不输出任何额外的字符;ends
向缓冲区插入一个空字符,然后刷新缓冲区。如果想在每次输出操作后都刷新缓冲区,我们可以使用
unitbuf
操纵符。它告诉流在接下来的每次写操作之后都进行一次flush
操作。而nounitbuf
操纵符则重置流,使其恢复使用正常的系统管理的缓冲区刷新机制:1
2
3cout << unitbuf; // 所有输出操作后都会立即刷新缓冲区
// 任何输出都立即刷新,无缓冲
cout << nounitbuf; // 回到正常的缓冲方式如果程序异常终止,输出缓冲区是不会被刷新的。当一个程序崩溃后,它所输出的数据很可能停留在输出缓冲区中等待打印。
当一个输入流被关联到一个输出流时,任何试图从输入流读取数据的操作都会先刷新关联的输出流。 既可以将一个
istream
对象关联到另一个ostream
,也可以将一个ostream
关联到另一个ostream
。每个流同时最多关联到一个流,但多个流可以同时关联到同一个ostream
。头文件
fstream
定义了三个类型来支持文件 IO:ifstream
从一个给定文件读取数据,ofstream
向一个给定文件写入数据,以及fstream
可以读写给定文件。创建文件流对象时,我们可以提供文件名(可选的)。如果提供了一个文件名,则
open
会自动被调用。在新 C++ 标准中,文件名既可以是库类型string
对象,也可以是 C 风格字符数组。在要求使用基类型对象的地方,我们可以用继承类型的对象来替代。这意味着,接受一个
iostream
类型引用(或指针)参数的函数,可以用一个对应的fstream
(或sstream
)类型来调用。也就是说,如果有一个函数接受一个ostream&
参数,我们在调用这个函数时,可以传递给它一个ofstream
对象,对istream&
和ifstream
也是类似的。如果我们定义了一个空文件流对象,可以随后调用
open
来将它与文件关联起来。如果调用open
失败,failbit
会被置位。因为调用open
可能失败,进行oppen
是否成功的检测通常是一个好习惯。一旦一个文件流已经打开,它就保持与对应文件的关联。实际上,对一个已经打开的文件流调用open
会失败,并会导致failbit
被置位。随后的试图使用文件流的操作都会失败。为了将文件流关联到另外一个文件,必须首先关闭已经关联的文件。一旦文件成功关闭,我们可以打开新的文件。当一个
fstream
对象离开其作用域时,与之关联的文件会自动关闭。编写函数,以读模式打开一个文件,将其内容读入到一个
string
的vector
中,将每一行作为一个独立的元素存于vector
中,将每个单词作为一个独立的元素存储在另一个vector
中。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
using namespace std;
int main()
{
ifstream input("license.txt");
vector<string> vs_line, vs_word;
string s;
if (input)
{
while (getline(input, s))
vs_line.push_back(s);
input.clear(); // 复位输入文件流状态为 true(输入文件流遇到文件尾状态会被置为 false)
input.seekg(0); // 返回输入文件流首位置
while (input >> s)
vs_word.push_back(s);
}
else
{
cerr << "Wrong input file stream." << endl;
return -1;
}
cout << "Output by line: " << endl;
cout << "" << endl;
for (auto iter = vs_line.begin(); iter != vs_line.end(); ++iter)
cout << *iter << endl;
cout << "" << endl;
cout << "" << endl;
cout << "Output by word: " << endl;
cout << "" << endl;
for (auto iter = vs_word.begin(); iter != vs_word.end(); ++iter)
cout << *iter << endl;
return 0;
}每个流都有一个关联的文件模式(file mode)。如下表所示:
文件模式 in 以读方式打开 out 以写方式打开 app 每次写操作均定位到文件末尾 ate 打开文件后立即定位到文件末尾 trunc 截断文件 binary 以二进制方式进行 IO 无论用哪种方式打开文件,我们都可以指定文件模式,调用
open
打开文件时可以,用一个文件名初始化流来隐式打开文件时也可以。指定文件模式有如下限制:- 只可以对
ofstream
或fstream
对象设定out
模式。 - 只可以对
ifstream
或fstream
对象设定in
模式。 - 只有当
out
也被设定时才可设定trunc
模式。 - 只要
trunc
没被设定,就可以设定app
模式。在app
模式下,即使没有显式指定out
模式,文件也总是以输出方式被打开。 - 默认情况下,即使我们没有指定
trunc
,以out
模式打开的文件也会被截断。为了保留以out
模式打开的文件的内容,我们必须同时指定app
模式,这样只会将数据追加写到文件末尾;或者同时指定in
模式,即打开文件同时进行读写操作。 ate
和binary
模式可用于任何类型的文件流对象,且可以与其他任何文件模式组合使用。
每个文件流类型都定义了一个默认的文件模式,当我们未指定文件模式时,就使用此默认模式。与
ifstream
关联的文件默认以in
模式打开;与ofstream
关联的文件默认以out
模式打开;与fstream
关联的文件默认以in
和out
模式打开。- 只可以对
默认情况下,当我们打开一个
ofstream
时,文件的内容会被丢弃。阻止一个ofstream
清空给定文件内容的方法是同时指定app
模式:1
2
3
4
5
6
7// 在这几条语句中,file1 都被截断
ofstream out("file1"); // 隐含以输出模式打开文件并截断文件
ofstream out2("file1", ofstream::out); // 隐含地截断文件
ofstream out3("file1", ofstream::out | ofstream::trunc);
// 为了保留文件内容,我们必须显式指定 app 模式
ofstream app("file2", ofstream::app); // 隐含为输出模式
ofstream app2("file2", ofstream::out | ofstream::app);WARNING:保留被 ofstream 打开的文件中已有数据的唯一方法是显式指定 app 或 in 模式。
sstream
头文件定义了三个类型来支持内存 IO,这些类型可以向string
写入数据,从string
读取数据,就像string
是一个 IO 流一样。istringstream
从string
读取数据,ostringstream
向string
写入数据,而头文件stringstream
既可从string
读数据也可向string
写数据。stringstream
特有的一些操作如下表所示:stringstream特有的操作 sstream strm; strm 是一个未绑定的 stringstream 对象。sstream 是头文件 sstream 中定义的一个类型 sstream strm(s); strm 是一个 sstream 对象,保存 strings 的一个拷贝。此构造函数是 explicit 的 strm.str() 返回 strm 所保存的 string 的拷贝 strm.str(s) 将 string s 拷贝到 strm 中,返回 void 每个 IO 对象都维护一组条件状态,用来指出此对象上是否可以进行 IO 操作。如果遇到了错误——例如在输入流上遇到了文件末尾,则对象的状态变为失效,所有后续输入操作都不能执行,直至错误被纠正。标准库提供了一组函数,用来设置和检测这些状态。
C++ Primer - 第 8 章 IO 库
猜你喜欢
Thank you for your donate!
- 本文链接: https://blog.shipengx.com/archives/f6bc4afa.html
- 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!