Concepts / Reading Numbers From the User With TextIO

Reading Numbers From the User With TextIO

TextIO reads from a file have no sensible recovery path, so when a read fails it throws an exception rather than fixing the input itself.

  • Programming

Why Keyboard Input and File Input Differ

When a program asks a user for a number, TextIO can keep the user in the loop. If the answer is not a legal number, the program can ask again, and the user simply types a new answer. A file is different. Nobody is sitting at the keyboard to retype a bad line, so the program has to decide what to do on its own. This article follows what happens inside TextIO when reading from a file goes wrong, and how a try..catch lets your program respond instead of stopping.

What do you think happens?

A program reads values from a file, hits a bad value, and no try..catch surrounds the read. What do you think happens?

  • The program asks the user to retype the value

  • The program fixes the value by itself and keeps going

  • An exception is thrown and, with nothing to catch it, the program stops with an error

Reveal answer
Answer:

An exception is thrown and, with nothing to catch it, the program stops with an error.

TextIO has no sensible recovery path for a file, so it reports the problem by throwing an exception. Only code written to catch that exception can respond to it differently.

What TextIO Does When a File Read Fails

TextIO can read several kinds of input, and a file is one of them. When the source is the user, TextIO checks that a numeric answer is legal and uses a loop-and-try..catch style of checking to get a valid value. When the source is a file, that recovery option is gone. There is no sensible way for TextIO to fix an illegal value sitting in the file, so it reports the problem by throwing an exception instead of correcting the input itself.

The sequence below shows the three parties involved. Your program asks for a value, TextIO reads from the file, and the file hands back something unusable. TextIO does not repair it. It throws the exception, and the exception travels back to your program, which decides how to respond.

Your programTextIOFile1. request next value2. read next item3. bad value or no data left4. throws IllegalArgumentException
Which component detects the failed read, which one decides how to respond, and how does the error move from the reader to the caller?

The Single Exception Type: IllegalArgumentException

Definition

TextIO throws only IllegalArgumentException for file errors, whatever the underlying problem is. Its design is deliberately simple: the error category does not change the exception type. An illegal value in the file and an attempt to read past the end of the data both arrive as the same kind of exception.

Catching a Failed Read With try..catch

A file error does not have to crash the program. If you have a better response than letting it crash, you wrap the reading code in a try block and attach a catch clause for IllegalArgumentException. The catch clause is where your alternative response goes. It might print a message, store a default value, or stop reading gracefully. Because TextIO uses only this one exception type, the catch clause you write is the one that receives every file-reading failure from TextIO.

The flowchart below traces control through a try..catch around a single read. Notice that the rest of the try block is skipped the moment the read fails, while the code after the whole try..catch still runs in both cases.

Enter try blockRead fails?Rest of try blockrunsCode after try..catchRest of try blockskippedcatch handlesIllegalArgumentExceptionnoyescontrol jumps
When a read fails, where does control jump, which code is skipped, and which code still runs afterward?
Worked example

Catching a bad value instead of crashing

A program reads a value from a file inside a try block. The value is illegal, so TextIO throws IllegalArgumentException. The program should store a default value of 0 and continue rather than stop.

  1. 1
    Wrap the read in a try block

    The reading code goes inside the try part, so any file-reading failure from TextIO is raised while control is inside this block.

  2. 2
    Attach a catch clause for IllegalArgumentException

    This is the only exception type TextIO throws for file errors, so this clause receives every file-reading failure from TextIO.

  3. 3
    Place the alternative response in the catch clause

    Inside the catch clause, the program stores the default value 0 in place of the value it could not read.

  4. 4
    Continue after the try..catch

    Once the catch clause finishes, control moves to the code after the whole structure, so the program keeps running.

Result

The program does not crash. The unreadable value is replaced by 0, and the remaining code runs normally.

java
Notes on the code
  • The try block holds the read; if the read fails, the statements after it inside the block are skipped.
  • The catch clause names IllegalArgumentException, the one type TextIO uses for file errors.
  • The statement after the closing brace always runs, whether the read succeeded or was caught.

Using the Exception as a Loop Stop Signal

A common file task is averaging numbers when you do not know how many numbers the file holds. The file contains only real numbers, and you want their sum and their average. Since the count is unknown, the program needs a way to decide when to stop. One approach is to keep reading without any planned stopping point and let the read eventually fail at the end of the file.

That failure is not really an error in this situation. Running past the last value is simply how the program learns that the data has ended. So the exception becomes a signal: you catch it, leave the reading loop, and finish the calculation. Putting the read inside a while (true) loop and breaking out when the exception arrives is an example of using an exception as part of the expected flow of control in a program, rather than as a sign that something went wrong.

Start loop passRead next valueRead succeeded?Add value to sum andcountIllegalArgumentExceptionthrowncatch breaks out ofloopCompute averageyesnext passno, data ended
In what order do the read, the loop check, the exception throw, and the exit happen on each pass, and at which step does the loop stop?
Worked example

Tracing a sum and average over a short file

The file contains the real numbers 3, 7, and 5, one per line. The program reads until the data runs out, then reports the sum and average.

  1. 1
    Pass 1: read 3

    The read succeeds, so 3 is added. Sum is 3, count is 1.

  2. 2
    Pass 2: read 7

    The read succeeds, so 7 is added. Sum is 10, count is 2.

  3. 3
    Pass 3: read 5

    The read succeeds, so 5 is added. Sum is 15, count is 3.

  4. 4
    Pass 4: read past the end

    No value remains, so TextIO throws IllegalArgumentException. The catch clause leaves the loop instead of adding anything.

  5. 5
    Compute the average

    The average is the sum divided by the count, which is 15 divided by 3.

Result

Sum is 15, average is 5.

Mistakes With Exception-Driven Reading

  • Common mistake

    Assuming the exception only means a bad value

    Why it fails

    TextIO throws the same IllegalArgumentException for a bad value and for reading past the end of the data, so the exception type alone does not say which happened.

    Do this instead

    Structure the handler so that it works correctly whichever situation caused the exception, or check the data state another way.

  • Common mistake

    Expecting TextIO to fix a bad value in the file

    Why it fails

    A file has no user at the keyboard to retype a line, and TextIO has no sensible recovery path, so it reports the problem instead of correcting it.

    Do this instead

    Write a catch clause that chooses the response, such as a default value, a message, or a graceful stop.

  • Common mistake

    Leaving the read unprotected in a loop with no catch

    Why it fails

    The end-of-data exception is then never intercepted, so the program stops with an error instead of finishing its calculation.

    Do this instead

    Wrap the read in a try block with a catch clause for IllegalArgumentException, and break out of the loop when the catch runs.

  • Common mistake

    Putting code that should run only after a successful read inside the try block below the read, where it will be skipped on failure

Practice Tracing a Read Loop

EASY

A file contains the real numbers 10, 4, and 6, one per line. A program reads with a while (true) loop, adds each value to a running sum and count, and breaks out of the loop when IllegalArgumentException is caught. Trace the passes: what is the sum, what is the count, and on which pass does the exception occur?

Hints
  • Count how many successful reads happen before the file runs out.
  • The exception occurs on the pass after the last value has been read.
  • The average is the sum divided by the count.

Key Takeaways

  • TextIO has no sensible recovery path for a bad value in a file, so it reports the problem by throwing an exception rather than fixing the input.
  • TextIO throws only IllegalArgumentException for file errors, and both a bad value and the end of the data arrive as that same exception type.
  • A try..catch that catches IllegalArgumentException lets your program choose its own response, such as printing a message, storing a default, or stopping cleanly.
  • A loop that reads until the data runs out can use the end-of-data exception as a deliberate stop signal, catching it and leaving the loop before finishing the calculation.