文件流对象的使用

文件流对象的使用

fstream 头文件定义了三种支持文件 IO 的类型:

1. ifstream,由 istream 派生而来,提供读文件的功能。

2. ofstream,由 ostream 派生而来,提供写文件的功能。

3. fstream,由 iostream 派生而来,提供读写同一个文件的功能。

fstream 类型除了继承下来的行为外,还定义了两个自己的新操作—— open和 close,以及形参为要打开的文件名的构造函数。fstream、ifstream 或ofstream 对象可调用这些操作,而其他的 IO 类型则不能调用。

假设ifile 和 ofile 是存储希望读写的文件名的 strings 对象,可如下编写代码:

// 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());

上述代码定义并打开了一对 fstream 对象。infile 是读的流,而 outfile则是写的流。为 ifstream 或者 ofstream 对象提供文件名作为初始化式,就相当于打开了特定的文件。

ifstream infile; // unbound input file stream
ofstream outfile; // unbound output file stream

上述语句将 infile 定义为读文件的流对象,将 outfile 定义为写文件的对象。这两个对象都没有捆绑具体的文件。在使用 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 对象与特定文件绑定。为了实现读写,需要将指定的文件打开并定位,open 函数完成系统指定的所有需要的操作。

C++ 中的文件名

由于历史原因,IO 标准库使用 C 风格字符串而不是 C++strings 类型的字符串作为文件名。在创建 fstream 对象时,如果调用open 或使用文件名作初始化式,需要传递的实参应为 C 风格字符串,而不是标准库 strings 对象。程序常常从标准输入获得文件名。通常,比较好的方法是将文件名读入 string 对象,而不是 C 风格字符数组。假设要使用的文件名保存在 string 对象中,则可调用 c_str 成员获取 C 风格字符串。

检查文件打开是否成功

打开文件后,通常要检验打开是否成功,这是一个好习惯:

// check that the open succeeded
if (!infile) {
cerr << "error: unable to open input file: "
<< ifile << endl;
return -1;
}

如果打开(open)失败,则说明 fstream 对象还没有为 IO 做好准备。

当测试对象:

if (outfile) // ok to use outfile?

返回 true 意味着文件已经可以使用。

将文件流与新文件重新捆绑

fstream 对象一旦打开, 就保持与指定的文件相关联。 如果要把 fstream 对象与另一个不同的文件关联, 则必须先关闭 (close)现在的文件, 然后打开 (open)另一个文件。如果已经打开, 则设置内部状态, 以指出发生了错误。接下来使用文件流的任何尝试都会失败。

ifstream infile("in"); // opens file named "in" for reading
infile.close(); // closes "in"
infile.open("next"); // opens file named "next" for reading

清除文件流的状态

考虑这样的程序, 它有一个 vector 对象, 包含一些要打开并读取的文件名,程序要对每个文件中存储的单词做一些处理。假设该 vector 对象命名为files,程序也许会有如下循环:

// for each file in the vector
while (it != files.end()) {
ifstream input(it->c_str()); // open the file;
// if the file is ok, read and "process" the input
if (!input)
break; // error: bail out!
while(input >> s) // do the work on this file
process(s);
++it; // increment iterator to get
next file
}

每一次循环都构造了名为 input 的 ifstream 对象,打开并读取指定的文件。

文件由构造函数打开,并假设打开成功,读取文件直到到达文件结束符或者出现其他的错误条件为止。在这个点上, input 处于错误状态。 任何读 input 的尝试都会失败。 因为 input是 while 循环的局部变量,在每次迭代中创建。这就意味着它在每次循环中都以干净的状态即 input.good() 为 true,开始使用。

如果希望避免在每次 while 循环过程中创建新流对象,可将 input 的定义移到 while 之前。这点小小的改动意味着必须更仔细地管理流的状态。如果遇到文件结束符或其他错误,将设置流的内部状态,以便之后不允许再对该流做读写操作。关闭流并不能改变流对象的内部状态。如果最后的读写操作失败了,对象的状态将保持为错误模式,直到执行 clear 操作重新恢复流的状态为止。调用 clear 后,就像重新创建了该对象一样。

如果打算重用已存在的流对象,那么 while 循环必须在每次循环进入时关闭(close)和清空(clear)文件流:

ifstream input;
vector<string>::const_iterator it = files.begin();
// for each file in the vector
while (it != files.end()) {
input.open(it->c_str()); // open the file
// if the file is ok, read and "process" the input
if (!input)
break; // error: bail out!
while(input >> s) // do the work on this file
process(s);
input.close(); // close file when we're done with it
input.clear(); // reset state to ok
++it; // increment iterator to get next file
}

如果忽略 clear 的调用,则循环只能读入第一个文件。要了解其原因,就需要考虑在循环中发生了什么:首先打开指定的文件。假设打开成功,则读取文件直到文件结束或者出现其他错误条件为止。在这个点上, input 处于错误状态。

如果在关闭(close)该流前没有调用 clear 清除流的状态,接着在 input 上做的任何输入运算都会失败。一旦关闭该文件,再打开 下一个文件时,在内层while 循环上读 input 仍然会失败——毕竟最后一次对流的读操作到达了文件结束符,事实上该文件结束符对应的是另一个与本文件无关的其他文件。

如果需要重用文件流读写多个文件,必须在读另一个文件之前调用 clear 清除该流的状态。

posted @ 2018-05-03 17:14  刘-皇叔  阅读(1944)  评论(0编辑  收藏  举报