Ordinary Output and Input Streams in C++
A file stream can be used as an ordinary stream: ofs acts as an ordinary ostream, just like cout, and ifs acts as an ordinary istream, just like cin.
Writing to a File With the Habits You Already Have
Most C++ programs that print something already rely on a stream. The object cout accepts output, and the object cin supplies input. A natural question follows: if a program wants to write to a file or read from one, does it need a completely different set of habits? The source answers this directly. A file stream can be used the same way as the ordinary stream objects you already know.
What do you think happens?
Given the statement ofs << "Total: " << total; where does the value of total end up?
Reveal answer
In the file that ofs refers to
The source says ofs can be used as an ordinary ostream, just like cout. The statement has the same form as one sending values to cout; only the destination changes, from the screen to the file.
This article follows one question through: what role does a file stream play in a program, and how closely does it match cout and cin?
Shared Stream Classes Behind cout and ofs
The reason the two objects behave alike is that they belong to related stream classes. The output file stream ofstream and the console object cout both work as output streams. Similarly, the input file stream ifstream and the console object cin both work as input streams. The diagram below shows these relationships: the general ostream and istream classes sit at the top, and the specific objects you use sit beneath them.
The source itself states only the behavioral claim: ofs acts as an ordinary ostream, and ifs acts as an ordinary istream. The class relationships above are the standard-library structure that makes that claim work in practice, which is why the same kinds of operations apply to both.
Output Through ofs Like cout
The source says that ofs can be used as an ordinary ostream, just like cout. In practical terms, a program can treat ofs as a destination for output in the same manner it treats cout. Code that sends values into cout can be understood as sending values into ofs, with the file as the final destination. The behavior of the statements does not need to be relearned; what changes is where the data lands.
Console: Total: 42
File report.txt contains: Total: 42- The cout line sends the text and value to the screen.
- The ofs line uses the same << operators and the same kind of statement.
- Only the destination differs: the file named
report.txtinstead of the screen.
The two statements in the example are identical in shape. A learner who already writes cout << value can write ofs << value without learning a new set of operators.
Tracing One Output Function Through Two Destinations
Because ofs is an ordinary ostream, a function can accept a generic output stream and write to whichever stream the caller supplies. The following generated example shows one function used with both cout and ofs. Notice that the function body never mentions the screen or the file.
Score: 90 (printed to the screen)
scores.txt now contains: Score: 90- The parameter out is a reference to a general ostream.
- The first call passes cout, so the text goes to the screen.
- The second call passes ofs, so the same text goes to the file.
Input Through ifs Like cin
The same reasoning applies in the opposite direction. The source says that ifs can be used as an ordinary istream, just like cin. A program that reads values from cin can therefore read values from ifs, with the file supplying the data instead of the keyboard. The input side mirrors the output side: ofs plays the role of an output stream, and ifs plays the role of an input stream.
(typed 7 at the keyboard)
Keyboard gave: 7
File gave: 15 (if numbers.txt begins with 15)- The cin line reads from the keyboard.
- The ifs line uses the same >> operator on a different source.
- The file supplies the data, so the value read depends on the file's contents.
Taken together, the two directions form one picture. Output goes from the program into a file through ofs. Input comes from a file into the program through ifs. In both cases the program interacts with a stream object in a way it already understands from cout and cin.
The Condition Behind the Comparison
The statement that ofs and ifs behave like cout and cin begins with a condition: assuming that the tests succeeded. The source does not spell out those tests in this passage, but the condition itself matters. The comparison with ordinary streams holds only when the file stream is in a working state. A learner should not assume that every file stream will behave like cout or cin in every circumstance; the claim is conditional.
String Streams in the sstream Header
File streams connect a program to a file. A different kind of stream connects a program to a string. The source states that the sstream header of the standard library provides streams to and from a string. This means the stream idea that applies to files and to the screen also has a counterpart that works with string data held in memory.
The source gives this topic only a brief mention and does not show the operations of string streams. What it does establish is the existence of streams to and from a string and the header that provides them, so a learner knows where to look next.
Mistakes Learners Make With Ordinary Streams
Common mistake Assuming every file stream behaves like cout or cin in every situation
Why it failsThe source attaches the comparison to the tests having succeeded, so the claim is conditional.
Do this insteadCheck the stream state after opening before relying on it as an ordinary stream.
Common mistake Relearning the statement form when switching from cout to ofs
What it looks likeofs << "Total: " << total;Why it failsThe source says the way the program sends data stays familiar; only the destination changes.
Do this insteadKeep the same << operators and change only the stream object's destination.
Common mistake Thinking a file read with ifs works the same way as cin regardless of the file contents
Why it failsThe value read with ifs comes from the file, so the result depends on what the file holds.
Do this insteadThink of ifs as cin with the file supplying the data instead of the keyboard.
Common mistake Treating the sstream header as if the source had already taught its string stream operations
Checking Your Understanding of Stream Roles
A function is written as void log(std::ostream& out) { out << "Done" << std::endl; }. Explain in your own words which two objects could be passed to log, and where the text "Done" would go in each case.
Hints
- Both cout and an ofstream object can be treated as an ostream.
- The destination depends on which object the caller passes in, not on the body of log.
Write a short program that opens an output file named notes.txt with an ofstream named ofs, then writes the line Hello to it using the same << form you would use with cout. Before writing, state what you would check to decide whether the comparison with cout still holds.
Hints
- Use the same statement shape as cout << "Hello".
- The source attaches the comparison to a working stream state, so mention that check.
Key Takeaways
- A file stream such as ofs can be used as an ordinary ostream, just like cout, so output statements keep the same form and only the destination changes to the file.
- A file stream such as ifs can be used as an ordinary istream, just like cin, so the program reads values the same way while the file supplies the data.
- The source makes this comparison conditional on the tests having succeeded, so the stream must be in a working state before it is treated as ordinary.
- The sstream header of the standard library provides streams to and from a string, extending the stream idea to in-memory string data; the source introduces this only briefly.