Details of file I/O seem to be buried at the back, missing, or overly complicated in most C++ manuals. This page provides a quick reference for the most frequently used methods.
This page only discusses basic options that seem to be common to all my C++ references. Apparently there is a lot of variation from one manual to another, and from one implementation to another. I believe the methods below to be safe and portable, at least for ASCII (human-readable text) files.
To do input and output, you will need to load the iostream header file. You may also need to load the fstream (file I/O) and/or iomanip (format manipulation) header files. Put some/all of the following lines at the top of your code file (or in the header file for your program, if you are using one).
#include <iostream> // I/O #include <fstream> // file I/O #include <iomanip> // format manipulation #include <string>
Three streams just exist: cout (terminal output), cin (terminal input), and cerr (error output, which also goes to the terminal).
When writing error messages, use cerr rather than cout. In simple examples, the two appear to be the same. However, they behave differently when you use Unix redirection operators when you run your program. In particular, if you redirect output to a file, error messages printed to cerr will still appear on the user's terminal whereas error messages printed to cout will be mixed into the output file.
File streams are of type ifstream (input) or ofstream (output).
ifstream fp_in; // declarations of streams fp_in and fp_out ofstream fp_out; fp_in.open("myfile.txt", ios::in); // open the streams fp_out.open("myfile.txt", ios::out); fp_in.close(); // close the streams fp_out.close();
A file should be closed if you are done with it, but the program will continue running for a while longer. This is particularly important when you intend to open a lot of files, as there may be a limit on how many you can have open at once. It is also a good idea if you intend to open a file for input, and then re-open the file for output.
Declaring the pointer and opening the file can be combined:
ifstream fp_in("myfile.txt", ios::in); // declare and open
The parameters ios::in and ios::out specify the mode in which the file is to be opened. Depending on the implementation of C++, it may be possible to specify a variety of other mode options, such as appending to the end of an existing file, triggering an error rather than overwriting an existing file, or specifying that a file is binary for operating systems (e.g. MS-DOS) which distinguish binary and ASCII files.
File streams must be passed to functions by reference, not by value.
void myfunction(ifstream &fp, ...) // use this void myfunction(ifstream fp, ...) // not this
If you pass streams by value, the C++ compiler will not complain. However, mysterious bad things will start happening, often in parts of the code which don't appear to be related to the offending function.
If each input item is surrounded by whitespace (blanks, tabs, newlines), the items can be read easily using the extraction operator >>.
int myinteger; // declarations float myfloat; char mychar; string mystring; fp_in >> myinteger; // input from file pointer or standard input cin >> myfloat; fp_in >> mychar; cin >> mystring;
The extraction operator works for numbers (ints, floats), characters (char), and strings (declared as arrays of type char or pointers to type char).
The extraction operator returns a zero value if it encounters a problem (typically, the end of the file). Therefore, it can be used as the test in an if statement or a while loop.
Numbers, characters, and strings can be written to a file, standard output, or the standard error using the insertion operator <<.
cout << "Value of myinteger " << myinteger << endl; cout << "My string is " << mystring << " plus a null character\n" << flush;
To insert a line break, either insert the magic variable endl or write the end-of-line character ('\n') to the output.
To make a pointer print out as a pointer, not as whatever type of data it points to, cast it to the type (void *). To make a character print as a number, cast it to type int. Similarly, you can use a cast to convince C++ to print an integer as the corresponding character.
cout << (void *)ptr; cout << (int)ch; cout << (char)ival;
When you send output to a stream, it does not necessarily get printed immediately. Rather, it may wait in a buffer until some unspecified event, e.g. buffer full enough, reading from input, or exit from program. The details may vary.
Buffering makes it faster to print a large amount of output, particularly if you are producing the output bit-by-bit (e.g. one character at a time). However, it is a nuisance for output used in debugging (e.g. a statement that informs you that step 3 of your algorithm has been finished).
Forcing all buffered output to actually be printed is known as "flushing" the stream. A flush can be forced by calling the flush function associated with each output stream, inserting the magic variable flush into the stream, or inserting endl.
cout << flush; cout.flush(); cout << endl;
All of the following are illustrated using the standard input, but they work just the same on file streams.
These operations all return zero if something goes wrong, e.g. they hit the end of the file. Therefore, they can be used as the condition in an if statement or while loop.
In addition, there are two more input operations, get and peek. These functions return EOF (which is secretly -1) if they encounter the end of the file. The output of these functions should be put into an integer (not a char) variable.
The following functions can be used to test the status of a stream. They return an integer, which is either zero or non-zero.
Notice that the stream's status will change to bad (not good, eof, etc) after the first read request which encounters a problem. So, to use one of these functions, you attempt to do what you wanted to do (e.g. open the file, read the next number from the file, ...). If the action can't succeed, the program won't crash, though some of your variables might not contain the values you intended. Next, use the status function to check whether the action succeeded.
Other options for writing data to an output stream are:
The function setf can be used to change formatting parameters for an output stream. For example, the following causes numbers to be left justified.
cout.setf(ios::left); \\ set option cout.unsetf(ios::left); \\ unset option
The most obviously useful parameters are:
The precision of numbers can be changed as follows. You can also set the width, i.e. the minimum number of spaces used to print the next. These features are used, for example, to make output items line up in columns when printed. Both of these features require that you include the iomanip header file.
cout << setprecision(2); \\ print two digits after decimal point cout.precision(2); \\ an alternative syntax cout << setw(8); \\ make item occupy 8 characters cout.width(8); \\ an alternative syntax
Setting the width to k forces the item to occupy at least k characters. If its printed representation is shorter, blanks will be added so that it occupies k characters. If its printed representation is longer, it will occupy more than k characters.
When you reset parameters such as the precision, it is not clear whether the new value lasts only for the next item printed or whether it persists for subsequent values. Apparently this varies with the parameter and the C++ compiler. Therefore, if you care about the value of some formatting parameter, explicitly set it to the right thing before you output each item.
For examples of usage, and how the formatting options affect the printing of numbers, look at Bob Keller's format sampler.
In general, it is possible to move to any position in a file stream. This is a capability most programmers use rarely, if at all. For fairly obvious reasons, don't try to use repositioning operations on the standard input, output, or error streams.
The most likely reason for using a repositioning command is to rewind a file to the start, so that you can read its contents again. This is done as follows:
fp_in.clear(); // forget we hit the end of file fp_in.seekg(0, ios::beg); // move to the start of the file
This page is maintained by Margaret Fleck.