Stream Iterators
istream_iterator and ostream_iterator connect file streams to algorithms and containers, and are usually passed as arguments rather than used on their own.
Why Stream Iterators Exist
Stream iterators are rarely used on their own. They are normally handed to algorithms as arguments. The algorithm does the real work, while the stream iterator quietly moves values between a stream and a container. In this article you will follow one small program that reads words from a file, puts them in order, drops repeated words, and writes what remains to a second file.
What do you think happens?
Suppose the input file contains the words: pear apple pear. What do you think the output file contains, and in what order?
Reveal answer
apple, pear
The words are gathered into a container, sorted into alphabetical order, and then copied with repeated values skipped. The sorted sequence is apple, pear, pear, and skipping the repeated pear leaves apple then pear, one word per line.
Keep one question in mind as you read: at which stage does the result stop matching what you expected? Most bugs in this style of program are not inside the algorithm itself. They sit at a boundary, such as where a value enters the program, where it is stored, or where it leaves.
Stream Iterators as Connectors
An istream_iterator reads values from an input stream. An ostream_iterator writes values to an output stream. Each one is an iterator, which means an algorithm can use it the same way it uses the iterators of a container.
The practical consequence is that the algorithm does not need to know that the other end of the iteration is a file. An algorithm sees a range of values to read from and a place to write to. Whether those values come from a vector, a set, or a file is hidden inside the iterator.
The program connects the two ends of a pipeline. On the input side, an ifstream is attached to a file, and an istream_iterator is built on that stream. On the output side, an ofstream is attached to another file, and an ostream_iterator is built on that stream. A vector or set sits between them as a buffer that holds the values while the algorithm works on them.
The algorithms never open a file. sort works on whatever range it is given, and unique_copy writes to whatever output iterator it receives. All file handling lives in the stream objects and their iterators. This keeps the algorithm calls short and identical in form to calls on in-memory containers.
The Full Program Step by Step
The first version of the program fills a vector from the input iterator, sorts it, and then copies it to the output with unique_copy. The three steps are separate, and each one hands its result to the next. Reading the program as a pipeline makes this clearer than reading it as a list of statements.
Contents of unique_words.txt for input words.txt containing: pear apple pear banana apple
apple
banana
pear- eos is a default-constructed
istream_iteratorand marks the end of the input - copy fills the vector from the input stream until eos is reached
- sort reorders the vector, which
unique_copydepends on unique_copywrites to the output iterator, which sends each word to the file
Tracing a Five-Word Input
The file words.txt contains: pear apple pear banana apple. Trace what each stage holds and what reaches unique_words.txt.
- 1Stage 1: read
The input iterator reads the five words in file order. The vector now holds pear, apple, pear, banana, apple.
- 2Stage 2: sort
sort rearranges the vector into alphabetical order: apple, apple, banana, pear, pear.
- 3Stage 3: unique_copy
unique_copywalks the sorted vector and skips repeated values. The first apple is written, the second apple is skipped, banana is written, and the first pear is written while the second is skipped. - 4Stage 4: delimiter
The
ostream_iteratorwrites a newline after each value, so each word lands on its own line.
unique_words.txt contains three lines: apple, banana, pear.
Notice that the vector holds the duplicates until the copy step. The sort is what places equal words next to each other, so that unique_copy can skip them as it writes. The file on disk is untouched until the last stage.
Reading the Declarations Closely
Two details in the declarations matter most. Both control what the program reads and what it writes, so both are common sources of surprises.
Vector Versus Set for Removing Duplicates
The vector version is longer than it needs to be. It reads the strings into a vector, sorts them, and then writes them out while skipping duplicates. A more direct approach is to never store duplicates in the first place. A set does this: it keeps its elements in order and does not keep duplicates.
Contents of unique_words.txt for input words.txt containing: pear apple pear banana apple
apple
banana
pear- The set replaces the vector, the sort, and
unique_copy - The first copy fills the set from the input stream
- The second copy writes the set's elements to the output stream
Because the set already holds its elements in order without duplicates, the sort step disappears and unique_copy is replaced by a simpler copy. The vector version needs three operations, while the set version needs two lines: one that fills the set and one that copies it out. The output file is the same in both cases, but the set version has fewer stages where something could go wrong.
Finding Where the Output Goes Wrong
When the output does not match what you predicted, follow the pipeline in order instead of rereading the algorithm call. Each stage has a clear input and output, so the first stage that disagrees with your expectation is the place to look.
- 1Confirm what the input iterator actually read from the file, word by word.
- 2Check what the container holds after the fill, before any sort.
- 3Check what the algorithm produced, such as the sorted vector or the set contents.
- 4Check what reached the output stream, including how each value was delimited.
Common mistake Looking for the bug inside the algorithm when the input is wrong
What it looks likeOutput is missing a word, and the programmer rewrites the unique_copy call without checking the file.
Why it failsThe algorithm only sees what the input iterator delivered. If a value was never read, no algorithm can recover it.
Do this insteadCheck the input stage first, then move forward through the pipeline.
Common mistake Forgetting that the sort comes before unique_copy
What it looks likeThe vector is filled and copied directly with unique_copy, so repeated words scattered through the file survive.
Why it failsThe three steps are a pipeline, and each one hands its result to the next. Skipping the sort changes the order the copy step sees.
Do this insteadKeep the fill, sort, and copy stages in that order, or use a set that stores values in order.
Common mistake Using an ordinary iterator as the end marker
What it looks likeThe end argument is built from the same file stream as the start argument, so it is not a default-constructed istream_iterator.
Why it failsReading stops when the iterator reaches the end marker, and the default-constructed eos is what represents the end of the input.
Do this insteadDeclare a default-constructed istream_iterator as the end marker and pass it as the second argument.
Common mistake Assuming the output is one word per line without a delimiter
What it looks likeThe ostream_iterator is built without the newline argument, so all words run together in the output file.
Why it failsThe second argument to
ostream_iteratoris written after each value. Without it, nothing separates the words.Do this insteadPass the delimiter, such as a newline, when constructing the ostream_iterator.
Practice Tracing a Pipeline
The input file contains: kiwi fig kiwi date fig kiwi. Trace the vector version of the program. What does the vector hold after the fill, after the sort, and what does unique_words.txt contain? Then say what a set version would write, and why.
Hints
- Write the six words in file order first, then sort them alphabetically.
- After sorting, equal words sit next to each other.
unique_copywrites only the first of each run. - A set stores each word once in order, so compare its contents with the unique values from the sorted vector.
Summary of the Pipeline
- 1
istream_iteratorandostream_iteratorconnect file streams to algorithms and containers, and are passed as arguments rather than used on their own. - 2The eos object, a default-constructed
istream_iterator, marks the end of input, and the second argument toostream_iteratoris written after each value. - 3The vector version fills, sorts, and copies with
unique_copy, while the set version stores ordered unique values and needs fewer steps. - 4When output is wrong, check each stage in order, starting at the input and ending at the output delimiter.
At a glance
istream_iteratorreads values from a stream andostream_iteratorwrites values to a stream, so algorithms can work with files the same way they work with containers.- The program fills a vector, sorts it, and writes it with
unique_copy, treating the three steps as a pipeline. - A set keeps elements in order without duplicates, replacing the sort and
unique_copywith a simpler copy. - To find a wrong output, trace the pipeline stage by stage from input to output delimiter and stop at the first stage that disagrees with your expectation.