The C++ libraries provide the ability to perform input and output using streams. A "stream" is a sequence of characters read or written from an I/O device or file. You've probably already tried using the three C++ standard streams: cin, cout, cerr and clog. This tech note will, hopefully, extend that knowledge and experience to file I/O streams.
File input or output usually involves an idiomatic sequence of operations:
We will provide code examples for both input and output. We will also briefly list format manipulator and stream methods that give more control over I/O conversion and stream behavior. We will start with output first as it is usually simpler to code.
Before doing any file stream-related operations, we must first include definitions for the C++ standard library fstream:
#include <fstream> using namespace std ;The second line is needed with later versions of the GNU C++ compiler (aka g++) in order to use the latest standard file stream library.
In order to write data to a file, we begin by defining a file output stream object:
ofstream outputStream ;Next, we must open the output stream and associate a file with the stream:
outputStream.open("filename") ;The open method takes one parameter -- the name of the file to open. If the file does not exist, it is created. If the file already exists, the contents are discarded.
Note: The open method may also take an optional second parameter which is the intended access mode: ios::in, ios::trunc, ios::out, ios::app, ios::ate, or ios::binary. Usually the default mode is sufficient.
The definition of a file input stream and opening of the stream can be combined into one definition:
ofstream outputStream("filename") ;This definition creates a new outputStream object and its constructor method opens the file named "filename" for writing.
Due to file and directory permission rules, the open method may fail. For example, the user may be running the program in a directory that does not permit writing by that user. The program should always check for failure. The value of outputStream will be zero (null) if the open method fails, so checking for failure is easy:
if (outputStream == 0) { cerr << "Error! Couldn't open output file for writing" << endl ; exit( 0 ) ; }In this case, the program displays an error message and calls the exit library function to terminate execution. Exit returns the value zero, which, by convention, signifies program failure. (Shell scripts can then use this exit status to control their own operation.) Other responses to failure are possible:
Note: Another way to check if the file was successfully opened is:assert(outputStream.is_open()) ;The error message generated by assert is not very informative and assert terminates the program. So, this method is not particularly desirable from the user's perspective.
Once the output file stream is open, the program can use the stream insertion operator, <<, to send values to the output stream for writing. The syntax is similar to working with cout:
int value ; ... value = 42 ; outputStream << "The answer is " << value << "." << endl ;
After writing data to the output file, you should close the file. This is accomplished by calling the stream's close method:
outputStream.close() ;The default destructor for the output stream will close the file. However, it's good programming practice to put one's toys away (e.g., close files, releaase storage, etc.) when done playing with them!
The example below pulls all of these parts together into a short program. Feel free to borrow from this program when working on your assignments. Try compiling the program and running it. You can exercise the error case by changing the permissions on the file "filename" to make it read-only. The open method will fail causing the error message to display and the program to exit.
#include <iostream> #include <fstream> using namespace std ; int main(int argc, char *argv[]) { ofstream outputStream ; int value ; outputStream.open("filename") ; if (outputStream == 0) { cerr << "Error! Couldn't open output file for writing" << endl ; exit( 0 ) ; } value = 42 ; outputStream << "The answer is " << value << "." << endl ; outputStream.close() ; }
File output streams support format manipulators that control the formatting of data during output. We've seen these same format manipulators before when working with the standard output and standard error streams.
dec | Use decimal representation for integer input or output |
hex | Use hexadecimal (base 16) representation for integer input or output |
octal | Use octal (base 8) representation for integer input or output |
flush | Flush the output stream |
endl | Insert a newline character into the output stream and flush the stream |
left | Left justify output |
right | Right justify output |
Other format manipulators are available by including the standard C++ library iomanip. The following table shows a few of the format manipulators from that library.
setprecision(n) | When displaying a float or double, show n places after the decimal point |
setfill(c) | Fill extra spaces in output fields with character c |
setw(n) | Set the width of the next output field to n places |
Output streams also support methods, which are functions that can be applied to an output stream. The table below describes a few of the methods that can be applied to an output stream like count, cerr and clog, as well as a file output stream.
ost.put(c) | Write the character c to the output stream ost |
ost.write(carray, n) | Write n characters of carray to the output stream |
ost.flush() | Flush the output stream's buffer |
Reading data from a file follows a similar pattern. However, we must also handle the case when the end-of-file has been reached and no more data items are available to be read. The code to include fstream, define an input file stream and check for success is similar to the code for output:
#include <iostream> #include <fstream> using namespace std ; ... ifstream inputStream ; inputStream.open("numbers") ; if (inputStream == 0) { cerr << "Error! Couldn't open input file for reading" << endl ; exit( 0 ) ; }This example opens the file named "numbers" for reading. The open method fail if the file does not exist or the program's user does not have permission to read the file.
Note: One way to test the error case, is to temporarily rename the file to something else to simulate a non-existent file. Or, set the the file permissions to disallow reading, e.g., chmod 0222 file. Then run the program and induce the error condition. Your error handling code will be invoked. Be sure to re-establish the original name or file permissions!
The input and output stream classes provide method functions that check the status of a stream.
The following code idiom reads a sequence of values from an input stream until the end-of-file is reached:
int value ; ... for( ; ; ) { ... inputStream >> value ; if (inputStream.eof()) break ; if (inputStream.fail()) { cerr << "Error! Failure while reading input stream" << endl ; break ; } ... }We also included a check for failure status as this condition is asserted when conversion fails due to bad or unexpected characters in the input stream. Without both the check for end-of-file and failure, the for loop will iterate forever. (This is, of course, bad.) The statement:
inputStream.close() ;closes the input stream.
Putting together the pieces, here is a short program that reads a sequence of integers from a file. The program counts the number of integer values that were read and computes the sum of the integers. When finished, the program displays the number read and the sum, and closes the input file.
#include <iostream> #include <fstream> using namespace std ; int main(int argc, char *argv[]) { ifstream inputStream ; int value, count, sum ; inputStream.open("numbers") ; if (inputStream == 0) { cerr << "Error! Couldn't open input file for reading" << endl ; exit( 0 ) ; } for( count = 0, sum = 0 ; ; ) { inputStream >> value ; if (inputStream.eof()) break ; if (inputStream.fail()) { cerr << "Error! Failure while reading input stream" << endl ; break ; } sum = sum + value ; count = count + 1 ; } cout << "Numbers read: " << count << endl ; cout << "Sum: " << sum << endl ; inputStream.close() ; }We tested the program with a file containing the integers from 0 to 11 and obtained the following output:
Numbers read: 11 Sum: 55When the input format of the fourth integer was changed from "3" to "3.0", the input conversion failed:
Error! Failure while reading input stream Numbers read: 4 Sum: 6The program displayed the error message and left the for loop early.
The following table describes a few of the format manipulators that can be used to control the conversion of data on input. These format manipulators can also be applied to the standard input stream, cin, since the standard input stream is just a specific kind of input stream. (This relationship is called "inheritance" because cin inherits these manipulators from the parent input stream class.)
dec | Use decimal representation for integer input |
hex | Use hexadecimal (base 16) representation for integer input |
octal | Use octal (base 8) representation for integer input |
A number of input stream methods are suppported. A few of these methods are summarized in the table below.
ist.get() | Read and return a single character for the input stream ist |
ist.get(carray, n, stop) | Read characters from the input stream into carray until n-1 characters have been read or the character stop is encountered; Terminate carray with a null character |
ist.getline(carray, n, stop) | Like get above, but remove the terminating character stop/t> from the input stream |
ist.read(carray, n) | Read up to n characters from the input stream into the character array carray |
ist.peek() | Return the next character to be read from the input stream, but leave the character in the stream (don't remove it) |
The following short program illustrates the use of the method get() to read individual characters from the file named "characters." The program echoes the characters to the screen. Try experimenting with the other input stream methods to see what they do and how they handle newline characters.
#include <iostream> #include <fstream> using namespace std ; int main(int argc, char *argv[]) { ifstream inputStream ; char c ; inputStream.open("characters") ; if (inputStream == 0) { cerr << "Error! Couldn't open input file for reading" << endl ; exit( 0 ) ; } for( ; ; ) { c = inputStream.get() ; if (inputStream.eof()) break ; if (inputStream.fail()) { cerr << "Error! Failure while reading input stream" << endl ; break ; } cout << c ; } inputStream.close() ; }Copyright © 2004-2013 Paul J. Drongowski