fstream 类型除了继承下来的行为外,还定义了两个自己的新操作—— open 和 close,以及形参为要打开的文件名的构造函数。fstream、ifstream 或 ofstream 对象可调用这些操作,而其他的 IO 类型则不能调用。
文件流对象的使用
1 2 3 4
// construct an ifstream and bind it to the file named ifile ifstream infile("in"); // ofstream output file object to write file named ofile ofstream outfile("out");
ifstream infile; // unbound input file stream ofstream outfile; // unbound output file stream infile.open("in"); // open file named "in" in the current directory outfile.open("out"); // open file named "out" in the current directory
警告: C++ 中的文件名
由于历史原因,IO 标准库使用 C 风格字符串而不是 C++ strings 类型的字符串作为文件名。在创建 fstream 对象时,如果调用 open 或使用文件名作初始化式,需要传递的实参应为 C 风格字符串,而不是标准库 strings 对象。程序常常从标准输入获得文件名。通常,比较好的方法是将文件名读入 string 对象,而不是 C 风格字符数组。假设要使用的文件名保存在 string 对象中,则可调用 c_str 成员获取 C 风格字符串。
infile.open(str1.c_str()); // open file named "in" in the current directory outfile.open(str2.c_str()); // open file named "out" in the current directory
检查文件打开是否成功
打开文件后,通常要检验打开是否成功,这是一个好习惯:
1 2 3 4 5 6
// check that the open succeeded if (!infile) { cerr << "error: unable to open input file: " << ifile << endl; return-1; }
ifstream infile("in"); // opens file named "in" for reading infile.close(); // closes "in" infile.open("next"); // opens file named "next" for reading
文件模式
在打开文件时,无论是调用 open 还是以文件名作为流初始化的一部分,都需指定文件模式(file mode)。每个 fstream 类都定义了一组表示不同模式的值,用于指定流打开的不同模式。与条件状态标志一样,文件模式也是整型常量,在打开指定文件时,可用位操作符设置一个或多个模式。文件流构造函数和 open 函数都提供了默认实参设置文件模式。默认值因流类型的不同而不同。此外,还可以显式地以模式打开文件。
in 打开文件做读操作
out 打开文件做写操作
app 追加模式:在每次写之前找到文件尾
ate 打开文件后立即将文件定位在文件尾
trunc 清空模式:打开文件时清空已存在的文件流
binary 以二进制模式进行 IO 操作
并不是所有的打开模式都可以同时指定。有些模式组合是没有意义的,例如同时以 in 和 trunc 模式打开文件,准备读取所生成的流,但却因为 trunc 操作而导致无数据可读。下表列出了有效的模式组合及其含义。
out 打开文件做写操作,删除文件中已有的数据
out | app 打开文件做写操作,在文件尾写入
out | trunc 与 out 模式相同
in 打开文件做读操作
in | out 打开文件做读、写操作,并定位于文件开头处
in | out | trunc 打开文件做读、写操作,删除文件中已有的数据
// output mode by default; truncates file named "file1" ofstream outfile("file1"); // equivalent effect: "file1" is explicitly truncated ofstream outfile2("file1", ofstream::out | ofstream::trunc); // append mode; adds new data at end of existing file named "file2" ofstream appfile("file2", ofstream::app);
outfile2 的定义使用了按位或操作符将相应的文件同时以 out 和 trunc 模式打开。
### 对同一个文件作输入和输出运算 ###
fstream 对象既可以读也可以写它所关联的文件。fstream 如何使用它的文件取决于打开文件时指定的模式。
默认情况下,fstream 对象以 in 和 out 模式同时打开。 **当文件同时以 in 和 out 打开时不清空** 。如果打开 fstream 所关联的文件时,只使用 out 模式,而不指定 in 模式,则文件会清空已存在的数据。如果打开文件时指定了 trunc 模式,则无论是否同时指定了 in 模式,文件同样会被清空。下面的定义将 copyOut 文件同时以输入和输出的模式打开:
1 2
// open for input and output fstream inOut("copyOut", fstream::in | fstream::out);
// opens in binding it to the given file ifstream& open_file(ifstream &in, conststring &file) { in.close(); // close in case it was already open in.clear(); // clear any existing errors // if the open fails, the stream will be in an invalid state in.open(file.c_str()); // open the file we were given return in; // condition state is good if open succeeded }
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 istringstreamstream(line); // bind to stream to the line we read while (stream >> word){ // read a word from line // do per-word processing } }
// str member obtains the string associated with a stringstream istringstreaminput_istring(format_message.str()); string dump; // place to dump the labels from the formatted message // extracts the stored ascii values, converting back to arithmetic types input_istring >> dump >> val1 >> dump >> val2; cout << val1 << " " << val2 << endl; // prints 512 1024
int ival; // read cin and test only for EOF; loop is executed even if there are other IO failures while (cin >> ival, !cin.eof()) { if (cin.bad()) // input stream is corrupted; bail out throw runtime_error("IO stream corrupted"); if (cin.fail()) { // bad input cerr<< "bad data, try again"; // warn the user cin.clear(istream::failbit); // reset the stream continue; // get next input } // ok to process ival }
// 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 对象,打开并读取指定的文件。构造函数的初始化式使用了箭头操作符 对 it 进行解引用,从而获取 it 当前表示的 string 对象的 `c_str` 成员。文件由构造函数打开,并假设打开成功,读取文件直到到达文件结束符或者出现其他的错误条件为止。
在这个点上,input 处于错误状态。任何读 input 的尝试都会失败。因为 input 是 while 循环的局部变量,在每次迭代中创建。这就意味着它在每次循环中都以干净的状态即 input.good() 为 true,开始使用。
如果希望避免在每次 while 循环过程中创建新流对象,可将 input 的定义移到 while 之前。这点小小的改动意味着必须更仔细地管理流的状态。如果遇到文件结束符或其他错误,将设置流的内部状态,以便之后不允许再对该流做读写操作。关闭流并不能改变流对象的内部状态。如果最后的读写操作失败了,对象的状态将保持为错误模式,直到执行 clear 操作重新恢复流的状态为止。调用 clear 后,就像重新创建了该对象一样。
如果打算重用已存在的流对象,那么 while 循环必须在每次循环进记得关闭(close)和清空(clear)文件流:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
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 }