When we want to read or write a file, we define a file stream object and associate that object with the file. Each file stream class defines a member function named open
that does whatever system-specific operations are required to locate the given file and open it for reading or writing as appropriate.
When we create a file stream, we can (optionally) provide a file name. When we supply a file name, open
is called automatically:
ifstream in(ifile); // construct an ifstream and open the given file
ofstream out; // output file stream that is not associated with any file
This code defines in
as an input stream that is initialized to read from the file named by the string
argument ifile
. It defines out
as an output stream that is not yet associated with a file. With the new standard, file names can be either library string
s or C-style character arrays (§ 3.5.4, p. 122). Previous versions of the library allowed only C-style character arrays.
fstream
in Place of an iostream&
As we noted in § 8.1 (p. 311), we can use an object of an inherited type in places where an object of the original type is expected. This fact means that functions that are written to take a reference (or pointer) to one of the iostream
types can be called on behalf of the corresponding fstream
(or sstream
) type. That is, if we have a function that takes an ostream&
, we can call that function passing it an ofstream
object, and similarly for istream&
and ifstream
.
For example, we can use the read
and print
functions from § 7.1.3 (p. 261) to read from and write to named files. In this example, we’ll assume that the names of the input and output files are passed as arguments to main
(§ 6.2.5, p. 218):
ifstream input(argv[1]); // open the file of sales transactions
ofstream output(argv[2]); // open the output file
Sales_data total; // variable to hold the running sum
if (read(input, total)) { // read the first transaction
Sales_data trans; // variable to hold data for the next transaction
while(read(input, trans)) { // read the remaining transactions
if (total.isbn() == trans.isbn()) // check isbns
total.combine(trans); // update the running total
else {
print(output, total) << endl; // print the results
total = trans; // process the next book
}
}
print(output, total) << endl; // print the last transaction
} else // there was no input
cerr << "No data?!" << endl;
Aside from using named files, this code is nearly identical to the version of the addition program on page 255. The important part is the calls to read
and to print
. We can pass our fstream
objects to these functions even though the parameters to those functions are defined as istream&
and ostream&
, respectively.
open
and close
MembersWhen we define an empty file stream object, we can subsequently associate that object with a file by calling open
:
ifstream in(ifile); // construct an ifstreamand open the given file
ofstream out; // output file stream that is not associated with any file
out.open(ifile + ".copy"); // open the specified file
If a call to open
fails, failbit
is set (§ 8.1.2, p. 312). Because a call to open
might fail, it is usually a good idea to verify that the open
succeeded:
if (out) // check that the open succeeded
// the open succeeded, so we can use the file
This condition is similar to those we’ve used on cin
. If the open
fails, this condition will fail and we will not attempt to use out
.
Once a file stream has been opened, it remains associated with the specified file. Indeed, calling open
on a file stream that is already open will fail and set failbit
. Subsequent attempts to use that file stream will fail. To associate a file stream with a different file, we must first close the existing file. Once the file is closed, we can open a new one:
in.close(); // close the file
in.open(ifile + "2"); // open another file
If the open
succeeds, then open
sets the stream’s state so that good()
is true
.
Consider a program whose main
function takes a list of files it should process (§ 6.2.5, p. 218). Such a program might have a loop like the following:
// for each file passed to the program
for (auto p = argv + 1; p != argv + argc; ++p) {
ifstream input(*p); // create input and open the file
if (input) { // if the file is ok, ''process'' this file
process(input);
} else
cerr << "couldn't open: " + string(*p);
} // input goes out of scope and is destroyed on each iteration
Each iteration constructs a new ifstream
object named input
and opens it to read the given file. As usual, we check that the open
succeeded. If so, we pass that file to a function that will read and process the input. If not, we print an error message and continue.
Because input
is defined inside the block that forms the for
body, it is created and destroyed on each iteration (§ 6.1.1, p. 205). When an fstream
object goes out of scope, the file it is bound to is automatically closed. On the next iteration, input
is created anew.
Exercise 8.4: Write a function to open a file for input and read its contents into a vector
of string
s, storing each line as a separate element in the vector
.
Exercise 8.5: Rewrite the previous program to store each word in a separate element.
Exercise 8.6: Rewrite the bookstore program from § 7.1.1 (p. 256) to read its transactions from a file. Pass the name of the file as an argument to main
(§ 6.2.5, p. 218).