File Stream Objects
- Describe the file objects for streaming sequences of data
- Describe how to write to and read from the same file object
- Describe how to store and retrieve images of memory without loss of information
"Files are examples of containers that you can both read from and write to. Consequently, you can have a stream that supports both
<<
and>>
. Such a stream is called aniostream
." Stroustrup (1997)
The streaming facilities for transfers to and from memory are defined in the input/output category of the Standard Library. A stream is defined as a sequence of items of unspecified size, while a byte stream is a sequence of bytes of unspecified size.
This chapter outlines the input-output stream class hierarchy of the C++ Standard Library, describes the file-stream classes in detail and introduces the member functions that access byte data within a file.
Class Hierarchy
The input-output stream class hierarchy supports streaming to and from the standard console devices, files, and string streams. The base class for this hierarchy is called ios_base
. It defines components that are independent of the direction of the stream. The basic_ios
class template holds the type-dependent information.
Two Class Hierarchies
The basic_ios
class template supports streams of char objects and wchar_t
objects through separate hierarchies.
ios
Class Hierarchy
The ios
class is the instance of the basic_ios
template for streams of type char
. The input (istream
) and output (ostream
) classes are abstract classes derived from this ios
class. Furthermore, the file input stream class (ifstream
) derives from the istream
class and the file output stream class (ofstream
) derives from the ostream
class. The fstream
class combines the input and output functionality and derives from the iostream
class, which derives from the istream
and ostream
classes.
The <fstream>
header file contains the definitions of all three file stream classes. All of the classes in this hierarchy are defined in the std
namespace.
wios
Hierarchy
The wios
class is an instance of the basic_ios
template for streams of type wchar_t
. The input (wistream
) and output (wostream
) classes are abstract classes derived from this wios
class. The file input stream class (wifstream
) derives from the wistream
class and the file output stream class (wofstream
) derives from the wostream
class. The wfstream
class combines the input and output functionality and derives from the wiostream
class, which derives from the wistream
and wostream
classes.
The <wfstream>
header file contains the definitions of all three file stream classes. All of the classes in this hierarchy are defined in the std
namespace.
ios_base
Class
The ios_base
class shared by both hierarchies holds formatting information, stream state flags, stream opening mode flags and the stream seeking direction flag. Its member functions provide access to these flags.
State Flags
Four flags identify a stream's current state:
ios_base::goodbit
is true if the next operation may succeedios_base::failbit
is true if the latest operation failedios_base::eofbit
is true if the stream object encountered an end of data markios_base::badbit
is true if the stream object encountered a serious error, possibly with the internal buffer
A stream is in a ready state if the first flag is true or the other three flags are false.
The eofbit
flag is set only after an attempt to read the stream has been made to read data when there is no more data to be read. Just having finished accessing the last byte in a stream does not set this eofbit
flag: it is the subsequent attempt to access a further byte that sets this bit.
Stream Seeking Direction
Three enumeration constants identify the seeking direction in a stream seeking operation
ios_base::beg
- beginning of the streamios_base::end
- end of the streamios_base::cur
- current position within the stream
These are public member constants and can be referred to throughout the inherited classes (ios::beg
) or their instantiated objects (cin.beg
).
basic_ios
Template
The basic_ios
template adds the fill character and holds the current state. Its member functions provide access to the state flags.
Member Functions
Six public member functions query the current state of the stream:
bool basic_ios::good() const
- none of the flags is setbool basic_ios::fail() const
-ios::failbit
orios::badbit
istrue
bool basic_ios::eof() const
-ios::eofbit
istrue
bool basic_ios::bad() const
-ios::badbit
istrue
bool basic_ios::operator!() const
- same result asfail()
basic_ios::operator bool() const
- same result as!fail()
Note that the logical negation operator (!
) is overloaded as an alternative to fail()
. This operator reports true
if the latest operation failed or if the stream has encountered a serious error. We can use it on a stream object to check the success of the most recent action:
if (std::cin.fail())
{
std::cerr << "Read error" << std::endl;
std::cin.clear();
}
// ... may also be written as ...
if (!std::cin)
{
std::cerr << "Read error" << std::endl;
std::cin.clear();
}
One member function resets the state of the stream:
void basic_ios::clear()
- clears all flags
Open mode Flags
Six member constants identify the open mode of a stream:
ios_base::in
open for readingios_base::out
open for writingios_base::app
open for appendingios_base::trunc
open for writing, but truncate if file existsios_base::ate
move to the end of the file when the file is openedios_base::binary
access the file as a binary file
ios
Classes
The ios
instance of the basic_ios
template defines the base class for narrow character (char
) input and output.
Practical bit-wise combinations of the openmode flags include
ios::in | ios::out
open for reading and writing (default)ios::in | ios::out | ios::trunc
open for reading and overwritingios::in | ios::out | ios::app
open for reading and appendingios::out | ios::trunc
open for overwriting
The ostream
and istream
bases classes for the ofstream
, ifstream
, and fstream
classes provide access to bytes within a narrow character stream through a buffer.
A data member of type streampos
holds the byte position value within the stream. Numbering starts at 0
for the first byte in the stream.
Output Stream
The member functions for accessing a byte within an output stream are:
streampos tellp()
- returns the current position in the output streamostream& seekp(streampos pos)
- sets the current position in the output stream topos
ostream& seekp(long offset, ios_base::seekdir dir)
- sets the current position in the output stream to offset fromdir
pos
refers to the number of bytes from the beginning of the stream, offset
refers to the number of bytes from the seeking direction, and dir
refers to one of the three enumeration constants (see above).
Note that the single argument version of seekp()
accepts the absolute position, while the two argument version accepts the relative position with respect to the specified seeking direction.
Input Stream
The member functions for accessing a byte within an input stream are:
streampos tellg()
- returns the current position in the input streamistream& seekg(streampos pos)
- sets the current position in the input stream topos
istream& seekg(long offset, ios_base::seekdir dir)
- sets the current position in the input stream to offset fromdir
pos
refers to the number of bytes from the beginning of the stream, offset refers to the number of bytes from the seeking direction, and dir
refers to one of three enumeration constants (see ios
class above).
Note that the single argument version of seekg()
accepts the absolute position, while the two argument version accepts the relative position with respect to the specified seeking direction.
wios
Class
The wios
instance of the basic_ios
template defines the base class for wide character (wchar_t
) input and output.
Practical bit-wise combinations of the openmode flags include
wios::in | wios::out
open for reading and writing (default)wios::in | wios::out | wios::trunc
open for reading and overwritingwios::in | wios::out | wios::app
open for reading and appendingwios::out | wios::trunc
open for overwriting
The wostream
and wistream
base classes for the wofstream
, wifstream
, and wfstream
classes provide access to bytes within a wide character stream through a buffer.
A data member of type wstreampos
holds the wide-character position value within the stream. Numbering starts at 0
for the first wide character in the stream.
Output Stream
The member functions for accessing a wide-character within an output stream are:
wstreampos tellp()
- returns the current position in the output streamwostream& seekp(wstreampos pos)
- sets the current position in the output stream topos
wostream& seekp(long offset, ios_base::seekdir dir)
- sets the current position in the output stream to offset fromdir
pos
refers to the number of bytes from the beginning of the stream, offset refers to the number of bytes from the reference position, and dir
refers one of the three seeking directions.
Note that the single argument version of seekp()
accepts the absolute position, while the two argument version accepts the relative position with respect to the specified seeking direction.
Input Stream
The member functions for accessing a wide-character within an input stream are:
wstreampos tellg()
- returns the current position in the input streamwistream& seekg(wstreampos pos)
- sets the current position in the input stream topos
wistream& seekg(long offset, ios_base::seekdir dir)
- sets the current position in the input stream to offset fromdir
pos
refers to the number of wide-characters from the beginning of the stream, offset refers to the number of wide-characters from the seeking direction, and dir
refers to one of three enumeration constants (see wios
class above).
Note that the single argument version of seekg()
accepts the absolute position, while the two argument version accepts the relative position with respect to the specified seeking direction.
File Objects
File stream objects manage the transfer of data between program memory and files through buffers. A file object can connect to a stream in either of two access modes:
- text mode - the stream consists of characters interpreted using an encoding sequence and has a record structure with a terminator that identifies the end of each record
- binary mode - the stream consists of characters without any interpretation or structure.
We specify the access mode on opening the file. Text mode is the default mode.
Connections
ofstream
objects manage writing to a file, while ifstream
objects manage reading from a file. Both classes have default constructors and constructors that receive the name of the file to be opened. The constructors are overloaded to receive the address of a C-style null-terminated string and a reference to a string
object. For file objects created using the default constructor, the open()
member function connects to the file itself. For example,
#include <fstream>
std::ofstream fout("output.txt"); // opens output.txt for output
std::ifstream fin ("input.txt"); // opens input.txt for input
std::ofstream fo; // declares an output file named fo
fo.open("moreOutput.txt"); // connect fo to moreOutput.txt
std::ifstream fi; // declares an input file named fi
fi.open("moreInput.txt"); // connects fi to moreInput.txt
The logical negation operator (!
) on the file object returns the current state of the object:
#include <fstream>
std::ofstream fout("output.txt"); // opens output.txt for output
if (!fout)
{
std::cerr << "File failed to open" << std::endl;
}
else
{
// opened successfully
}
The is_open()
query on the file object returns the success of a connection attempt:
#include <fstream>
std::ofstream fout("output.txt"); // opens output.txt for output
if (!fout.is_open())
{
std::cerr << "File failed to open" << std::endl;
}
else
{
// opened successfully
}
The open-mode flags listed above, if passed to the constructor or open()
specify the connection mode.
File Buffer
The rdbuf()
member function on a file object returns the address of the object's buffer. The insertion operator (<<
) is overloaded for a pointer to this buffer. To copy the contents of a file (say, source) to another file (say, destination) we can call this member function directly:
// Copying Files
// fileCopy.cpp
#include <fstream>
int main(int argc, char agrv[])
{
std::ifstream source(argv[1]);
std::ofstream destination(argv[2]);
destination << source.rdbuf();
}
Close a Connection
The destructor for a file object flushes the buffer and closes the file when the object goes out of scope. To close a file connection before the file object goes out of scope, we call the close()
member function on the object itself:
// Closing a connection early
// fileCopy2.cpp
#include <fstream>
int main(int argc, char agrv[])
{
std::ifstream source;
std::ofstream destination(argv[3]);
source.open(argv[1]); // open first file
destination << source.rdbuf(); // copy
source.close(); // close first file
source.open(argv[2]); // open second file
destination << source.rdbuf(); // copy
}
Direct Access
A file object can access the contents of its stream either sequentially or directly. Sequential access progresses from the beginning of the stream to its end. Direct access starts at a specified position in the stream. The seek?()
member functions on the file object set the starting position. That position is the next to be accessed within the stream. The program below
// File Objects - Direct Access
// direct.cpp
#include <iostream>
#include <fstream>
int main(int argc, char* argv[])
{
std::ofstream fo(argv[1]); // open for output
fo << "Line 1" << std::endl; // line 1
fo << "Line 2" << std::endl; // line 2
fo << "Line 3" << std::endl; // line 3
fo.seekp(0, std::ios::beg); // go to the start of the file
fo << "****"; // overwrite four characters
fo.seekp(4, std::ios::cur); // go 4 char's beyond current
fo << '#'; // overwrite one character
std::streampos p = fo.tellp(); // remember current position
fo.seekp(0, std::ios::end); // go to end of the file
fo << "The last line\n"; // add a line
fo.seekp(p); // jump back to pos
fo << '^'; // overwrite one character
fo.close(); // close file
std::ifstream fi(argv[1]); // open for reading
char c;
while (fi.get(c)) // read 1 character at a time
std::cout << c; // display the character
fi.clear(); // clear failed (eof) state
fi.seekg(-8, std::ios::end); // move 8 bytes from end
while (fi.get(c)) // read 1 character at a time
std::cout << c; // display the character read
}
outputs in Windows
**** 1
#^ne 2
Line 3
The last line
t line
and in Linux
**** 1
L#^e 2
Line 3
The last line
st line
The output on a Windows platform differs from that on a Linux platform because the record terminator consists of two characters on Windows - a carriage return and a newline (\r\n
) - while the record terminator on Linux is a single character - a newline (\n
).
Writing and Reading
A file object of the fstream
class can manage both writing to and reading from a file. Instead of creating separate objects for reading and writing, we can create a single instance of the fstream
class for writing to and reading from the same file.
The program below
// File Objects - writing and reading
// fstream.cpp
#include <iostream>
#include <fstream>
int main(int argc, char* argv[])
{
std::fstream f(argv[1], std::ios::in | std::ios::out | std::ios::trunc);
f << "Line 1" << std::endl; // line 1
f << "Line 2" << std::endl; // line 2
f << "Line 3" << std::endl; // line 3
f.seekp(0, std::ios::beg); // go to the start of the file
f << "****"; // overwrite four characters
f.seekp(4, std::ios::cur); // go 4 bytes beyond current
f << '#'; // overwrite one character
std::streampos p = f.tellp(); // remember current position
f.seekp(0, std::ios::end); // go to end of the file
f << "The last line\n"; // add a line
f.seekp(p); // jump back to pos
f << '^'; // overwrite one character
char c;
f.seekg(0, std::ios::beg); // to the start of the file
while (f.get(c)) // read 1 character at a time
std::cout << c; // display the character
f.clear(); // clear failed (eof) state
f.seekg(-8, std::ios::end); // move 8 bytes from end
while (f.get(c)) // read 1 character at a time
std::cout << c; // display the character read
}
outputs in Windows
**** 1
#^ne 2
Line 3
The last line
t line
and in Linux
**** 1
L#^e 2
Line 3
The last line
st line
Note the open-mode flag settings for writing to and reading from the file.
Binary Access
Binary access transfers data to and from memory without any formatting; there is no conversion, insertion, or extraction of record or field separators. Binary access lets us save the image of the contents of memory without any loss of information. The contents of a file opened for binary access is typically indecipherable when viewed within a text editor.
An application that reads or writes in binary access mode is responsible for keeping track of any structure that the data may have.
Input
The istream
class of the input-output hierarchy includes a member function that reads a stream in binary mode. The prototype for this function is
std::istream& read(char* data, std::streamsize nb);
data
is the address of the destination in memory where the file data is to be copied and nb
is the number of bytes to be copied. std::streamsize
is an integral type typically type defined as a signed long
.
The program on the left reads from the file named on the command line, stores the data in str
and displays the stored string on standard output
// Binary Access - Reading
// readBinary.cpp
#include <iostream>
#include <fstream>
int main(int argc, char* argv[])
{
char str[1025];
char* p = str;
std::ifstream f(argv[1], std::ios::in | std::ios::binary);
while(f)
f.read(p++, 1);
*--p = '\0'; // adds the null terminator
std::cout << str << std::endl;
}
Hello World
The std::ios::binary
flag in the constructor call specifies binary access mode.
Output
The ostream
class of the input-output hierarchy includes a member function for writing to a file in binary mode. The prototype for this function is
std::ostream& write(const char* data, std::streamsize nb);
data
is the address of the data to be copied to the file and nb
is the number of bytes to be copied.
The following program writes the string str
to the file named on the command line
// Binary Access - Writing
// writeBinary.cpp
#include <fstream>
int main(int argc, char* argv[])
{
char str[] = "Hello World";
std::ofstream f(argv[1], std::ios::out | std::ios::binary | std::ios::trunc);
f.write(str, sizeof str - 1);
}
Binary versus Text
The program below compares binary and text access with respect to the same value stored in memory (1.0/3.0). The file object opened under binary access preserves the original precision so that the value returned to memory is the same as originally stored. The file object opened under text access discards some of the precision as it formats the value for output to 6 decimal places.
// Binary Access - Binary or Text
// binary.cpp
#include <iostream>
#include <iomanip>
#include <fstream>
int main(int argc, char* argv[])
{
std::cout << std::fixed << std::setprecision(15);
{
// binary access
double x = 1.0/3.0;
std::fstream f(argv[1], std::ios::in | std::ios::out | std::ios::trunc | std::ios::binary);
f.write(reinterpret_cast<char*>(&x), sizeof(x));
f.seekg(0, std::ios::beg);
f.read(reinterpret_cast<char*>(&x), sizeof(x));
std::cout << x << std::endl;
}
{
// text access
double x = 1.0/3.0;
std::fstream t(argv[2], std::ios::in | std::ios::out | std::ios::trunc);
t << x;
t.seekg(0, std::ios::beg);
t >> x;
std::cout << x << std::endl;
}
}
0.333333333333333
0.333333000000000
Note the cast to a char*
type in the read()
and write()
member functions.
Exercises
- Read Wikipedia on streams