Concepts / Stream Formatting and Buffering

Stream Formatting and Buffering

An iostream has a state that can be examined to tell whether the last operation succeeded.

  • Programming

Predicting When a Read Loop Stops

An iostream carries a state that you can examine to determine whether an operation succeeded. The most common use of that state is reading a sequence of values from a stream. A typical reading function loops over the input, stores each value it reads, and stops when a read fails. The question this article answers is simple to ask and easy to get wrong: when the loop finally stops, why did it stop, and what has the container received up to that point?

What do you think happens?

The stream contains the text 1 2 x 3. The first two items are integers, the third is not, and a fourth integer follows the bad item. A loop reads integers into a vector one at a time, using the stream itself as the loop condition. What does the vector contain when the loop finishes?

  • 1 2 3

  • 1 2

  • Nothing, because the input contains a bad token

Reveal answer
Answer:

1 2

The loop stores 1 and then 2. When it tries to read x as an integer, the stream is no longer ready, the loop condition becomes false, and the loop exits. The 3 that follows x is never read, even though it is a valid integer, because the loop never gets past the bad item.

The Read Loop and Its Single Condition

The function below reads integers from an input stream into a vector and returns the vector. It is a reconstruction of the pattern described in the source: a single while condition does all the work. The loop does not count values, and it does not look for an end marker in the data.

Code walkthrough
vector<int> read_ints(istream& is) {
    vector<int> v;
    int i;
    while (is >> i) v.push_back(i);
    return v;
}
  1. 1
    The function creates an empty vector named v to hold the results.
  2. 2
    It declares an int named i to receive each value read from the stream.
  3. 3
    The expression is >> i attempts to read one integer. It returns a reference to is, and testing that stream yields true only if the stream is ready for another operation.
  4. 4
    When the read succeeds, the value is stored in v by push_back and the loop goes around again.
  5. 5
    When the next item is not an integer, the stream is no longer ready, the condition is false, and the loop exits.
  6. 6
    The function returns v, which holds only the values read before the first failure.
  • vector<int> v holds the successful reads
  • int i is the variable the extraction operator writes into
  • while (is >> i) is the only condition in the loop

Each pass through the loop asks one question: did the stream accept another integer? The answer comes from the stream's state, not from a counter or a sentinel value. This is why the loop is so short, and also why it is easy to misjudge. The condition is not checking whether the data is valid in any broader sense. It is checking whether the most recent read succeeded.

What the Stream State Records

The I/O state of a stream holds the information needed to read or write. It includes formatting information, the error state (for example, whether end of input has been reached), and the kind of buffering in use. For error handling, the part that matters is the error state, because it is what the readiness test reports on. When the state says the stream is ready, the next operation is attempted. When it says the stream is not ready, the operation is not attempted and the loop condition evaluates to false.

The same stream can be in different conditions at different points in a program. It starts ready, it can reach the end of input, it can hit an error, and an error that was not serious can be cleared so that the stream is ready again. The diagram below shows these conditions as states and the events that move the stream between them.

Readynext read is attemptedEnd of inputusual loop exitStoppedserious error, reading endsError recordedread was not an expectedvalueReady againnon-serious error clearedread succeeds, value storedend of input reachedvalue of wrong typeerror is seriouserror is not seriousstate cleared
Which event moves the stream out of the ready state, and what can bring it back? A stream that is ready accepts the next read. Reaching the end of input is a terminal condition. A wrong-type read sets an error, which is either serious and stops reading, or not serious and can be cleared back to ready.

Notice that end of input is drawn as a terminal condition. Reaching it is the usual reason a read loop finishes. An error, by contrast, leads to a decision. If the error is serious, reading stops. If it is not serious, the state can be cleared and the stream returns to ready. The source describes the state as something a user can set to record that an error has occurred, and clear if the error was not serious. This gives the program a real choice: a failed read can end the whole operation, or it can be recorded, handled, and followed by further reading.

Tracing the Read of 1 2 x 3

Now trace the loop on the input 1 2 x 3, one iteration at a time. Track the value read, whether the condition is true, and what the vector holds afterward. The trace shows exactly where the result diverges from what a reader might expect, which is the central skill this article is building.

Read 1condition true, store 1Read 2condition true, store 2Read xcondition false, loop exitsVector: 1, 23 is never readnext passnext passloop ends
On each pass, what does the condition test, and where does the loop stop? The first two reads succeed and store values. The third read finds x, the condition becomes false, and the loop exits with the vector holding 1 and 2.
Worked example

Finding the stopping point in 1 2 x 3

Given the input 1 2 x 3 and the read_ints loop, determine what the vector contains and identify the iteration where the loop stops.

  1. 1
    Iteration 1

    The extraction reads 1. The stream is ready, so the condition is true. The value 1 is stored in the vector.

  2. 2
    Iteration 2

    The extraction reads 2. The stream is still ready, so the condition is true. The value 2 is stored, and the vector now holds 1 and 2.

  3. 3
    Iteration 3

    The extraction tries to read x as an int and fails. The stream is no longer ready, so the condition is false and the loop exits. Nothing is stored.

  4. 4
    After the loop

    The function returns the vector. The 3 after x was never read, because the loop does not skip bad items and does not resume reading.

Result

The vector contains 1 and 2. The loop stopped at the third iteration, where the read of x failed.

Recording and Clearing a Non-Serious Error

The read_ints function as written stops at the first bad token, because it has no step for recovery. Sometimes a program should keep reading after a bad item, for example to skip a stray word and collect the integers that follow it. The source states that a user can set the stream's state to record that an error has occurred, and can clear that state if the error was not serious. Once the state is cleared, the stream is ready for further operations.

Read failswrong type recordedClear statestream ready againDiscard bad inputremove the bad tokenRead againreading continuesnot seriousthenthen
After a type-mismatch failure sets the error state, what sequence lets the stream accept input again? The program clears the recorded state, discards the bad input so it is not read again, and then continues reading.

The decision to clear depends on whether the error is serious. The source leaves that judgment to the program. A failed read that makes the rest of the operation meaningless should stop reading. A failed read that affects only one item can be recorded, handled, and followed by further reading.

Where Buffered Text Fits In

Between the input source and the variable being read, characters are held in a buffer. The source identifies buffering as one part of a stream's I/O state, alongside formatting information and the error state. Leftover text that is not consumed by one read remains available to the next read. This is why a bad token that is not removed from the input can cause the next operation to fail in the same way. The source gives this topic less detail than the state and loop behavior, so the key practical point is that a failed read does not automatically clear the text that caused it.

Mistakes in Reading Loop Results

  • Common mistake

    Assuming the loop reads every value in the input

    What it looks like

    With 1 2 x 3, expecting the vector to hold 1, 2, and 3

    Why it fails

    The loop stops at the first failed read. The 3 is never reached because the condition is false at x.

    Do this instead

    Trace the loop one iteration at a time and find the first read that fails.

  • Common mistake

    Expecting the loop to skip a bad item and continue

    What it looks like

    Believing that x is discarded automatically and reading resumes at 3

    Why it fails

    The function has no step that clears the state or discards input, so the stream is not ready after the failure.

    Do this instead

    Add explicit recovery steps if reading must continue: clear the state and discard the bad input.

  • Common mistake

    Treating the returned vector as the full input

    What it looks like

    Reading a vector of two values and assuming the input had only two values

    Why it fails

    The vector is only a record of the successful reads. The input may have contained many more values after the stopping point.

    Do this instead

    When the result is short, look at the input at the position where the stream stopped being ready.

  • Common mistake

    Ignoring that the loop condition tests readiness, not data validity

Locating the Iteration Where Results Diverge

When a read loop returns something you did not expect, the debugging task is to find the iteration where the stream stopped being ready. Ask what the input held at that point, not what the loop was supposed to produce. The first failed read is where the result diverged from your expectation, and everything the loop would have read after that point is missing from the result.

MEDIUM

The input is 4 5 6.5 7 8. The read_ints loop from this article runs on it. Before checking, predict the contents of the vector, then identify the iteration where the first divergence from the expected full list of integers occurs.

Hints
  • Follow the loop one read at a time and note the value of the condition after each read.
  • The value 6.5 is not an int. Consider what the extraction operator does with the .5 part, and whether the stream remains ready afterward.
  • The vector is only a record of successful reads, so everything after the first failure is absent from it.
HARD

Modify read_ints so that a non-integer token is cleared and discarded, and reading continues until the end of input. State in one sentence what the modified loop does on a bad token, and name which step must happen before the next read.

Hints
  • Consider the order of the steps from the recovery diagram: clear, discard, then read again.
  • The loop must still stop cleanly at the end of input.

Key Takeaways

  • An iostream has a state that can be examined to tell whether the last operation succeeded, and a read loop's condition tests exactly that readiness.
  • A loop such as while (is >> i) stops at the first value that is not the expected type, typically end of input, and values after that point are not read.
  • A bad token in the middle of the data stops the loop in the same way as the end of input, so the vector holds only the reads that succeeded.
  • A program can record an error in the stream's state and clear it if the error is not serious, but the bad input must also be discarded before reading can usefully continue.
  • When a result differs from expectation, locate the first failed read and examine the input at that position, because everything after it is missing from the result.

Related concepts

More concepts starting with S