If we imagine a stream object as a hose, opening the stream is like attaching one end of the hose to a tap or a sprinkler, and the bytes of data moving through the stream object is like the water flowing through the hose. One end of an open stream is attached to the program, and the other is attached to a file. The console I/O objects, cin
, cout
, and cerr
are instantiated and connected to the console, which is just a special file, before a program runs. Once created and opened (i.e., connected to a file), the file I/O objects behave just like cin
and cout
, which means that we already know quite a bit about how to use them.
#include <iostream> #include <fstream> using namespace std; |
(a) |
|
(b) |
(c) |
fstream
also includes iostream
, but if the program file also performs console I/O, it should also include iostream
for clarity.fstream
class is more flexible - programs can both read and write files through instances of this class - it is more complicated to use, so we'll focus most of our attention on ifstream
and ofstream
.ifstream
attaches one end of the stream to an input file; opening an instance of ofstream
attaches one end of the stream to an output file.file_name
represents a file's name. In simple cases the name may be a string literal (a), but it is most often a string variable - either a C-string (b) or an instance of the string class (c). Most operating systems typically do not permit some characters in the directory or file names; the excluded characters depend on the specific system.
"payments.txt"
char* file_name;
string file_name
Each stream object has several behaviors that control how they treat or process the data as it moves between the program and the associated file. Each stream behavior is represented by, and is stored in the object, as a one-bit flag. Each flag is like a light switch: it's either it's on, enabling the behavior, or it's off, disabling the behavior. Together, these behaviors are the modes associated with every open file.
File modes really consist of a bit-mask or a bit-vector where each bit is one of the file's modes. (Stream objects also have flags that govern how data is formatted - these are controlled with the setf
function - and flags that "remember" the current state of the stream.) C++ defines a pseudo data type called openmode
that is used to represent the specific bit-masks. The behaviors are set when the file is opened, either with a constructor or with an open function call. The mode argument of the constructors and the open functions each have a default value that is based on the specific stream class and which makes it possible to call the functions with a single argument.
ios::in | Open file for input |
ios::out | Open file for output |
ios::app |
Append data to the end of the output file (file-pointer repositioning commands are ignored, forcing all output to take place at the end of the file) |
ios::ate |
Open the file at the end of the data (allows the file-pointer to be repositioning within the file) |
ios::trunc | Truncates or discards the current contents of existing files |
ios::binary |
Opens the file in binary mode (without this mask, the file is opened in text mode by default) |
Unlike the console stream objects that are ready for use as soon as the program starts, instances of the <fstream>
classes must be instantiated and associated with a file before a program is able to use them. The file stream classes (green) define a family of overloaded constructors. Some of the constructors associate the new stream object with (i.e., open) a specific file at the same time that the stream object is instantiated. But each stream class also defines a default constructor that creates a new stream object but does not associate it with or open a file. In the latter case, the stream member function open
is called to access a specific file.
ifstream input(file_name); // use file // "input" closed by destructor |
ifstream input; while (....) { input.open(file_name); //use file input.close(); } |
(a) | (b) |
open
member function. Each open file uses some system resource, so the operating system limits the number of files that a program may have open at any time. For this reason, it is important that a program closes a file when it is finished with it.
close
member function, but the stream classes also define destructors that safely close the file before destroying the stream object.Stream classes define a default constructor that creates a stream object, but the constructor does not open or associate the stream with a file. The classes also define constructors with two arguments. The first argument is the file name that the constructor opens and binds to the stream. The second argument is the file's modes, which the constructor sets when opening the file. The second argument has a default value, which means that the constructor may be called with either one or two parameters.
ifstream input; ifstream input(file_name); |
ofstream output; ofstream output(file_name); ofstream output(file_name, ios::app); |
fstream file; fstream file(file_name); |
(a) | (b) | (c) |
input >> height;
. When the file is opened, it must exist or the open operation will fail.
output << height;
. If the file exists and contains data, then the fate of the existing data depends on the modes parameter when the file is opened.
ios::app
flag (as the modes parameter) causes the stream object to retain any existing data and append the new data at the end of the file.The file stream constructor arguments are the same as the open
member function, which suggests the following equivalences:
ifstream input(file_name); |
ifstream input; input.open(file_name); |
(a) | |
ifstream input(file_name, ios::binary); |
ifstream input; input.open(file_name, ios::binary); |
(b) | |
ofstream output(file_name); |
ofstream output; output.open(file_name); |
(c) | |
ofstream output(file_name, ios::app); |
ofstream output; output.open(file_name, ios::app); |
(d) | |
ofstream output(file_name, ios::app | ios::binary); |
ofstream output; output.open(file_name, ios::app | ios::binary); |
(e) | |
fstream output(file_name, ios::in | ios::out); |
fstream output; output.open(file_name, ios::in | ios::out); |
(f) |
Each stream object maintains a set of four one-bit flags that indicate the stream's current state or condition. File operations like opening, reading from, or writing to a file can change the stream's state. The ios
class defines several member functions that report the stream's state based on the current flag settings. The stream subclasses inherit these reporting functions. In a Java program, most file operations occur inside a try-catch structure, and the program detects errors when an operation throws an exception. C++ takes a different approach requiring programmers to test a stream's state flags explicitly. When programmers open a file with a stream object, they validate the stream is ready with one of the inherited functions.
Of the four flags, the goodbit is the most inclusive. The good()
function returns true
only if there are no errors on the stream - indicating that a newly opened file is ready for reading or writing. If any of the error or end-of-file flags are set, good()
returns false
. Read and write operations can set two error flags. Programmers can test those flags with the bad()
and fail()
and fail functions. The next section covers the end-of-file bit, eofbit, and the eof()
function. Please see the "State flag functions" for more detail.
ifstream file(file_name); if (! file.good()) { cerr << "Unable to open " << file_name << endl; exit(1); //return; } // process the file |
ifstream file(file_name); if (file.good()) { // process the file } else { cerr << "Unable to open " << file_name << endl; exit(1); //return; } |
ifstream
, ofstream
, or fstream
.