Skip to content
ExamSpine

Lesson 6.4

Catch and recover

Part 6: Errors, testing and filesReading, about 6 min2 exercises in this lesson

Java answers can't run on Spine yet. You can still read Java, trace it, and put an answer's lines in order.

Catch and recover

By the end of this you will be able to catch an exception, so that a bad value typed by the user no longer stops your program, and choose what the program does instead.

In the last lesson, a line that turned the word two into a whole number stopped the whole program. That is the right thing to happen when nobody planned for it. But a user typing a word where a number belongs is something you can plan for. Catching the exception is how.

Try, and say what to do if it fails

A kitchen timer asks how many minutes to count. If what is typed is not a whole number, it says so and counts 5 minutes instead.

minutes = 5
text = input()
try:
    minutes = int(text)
    print("Timer set")
except ValueError:
    print("Not a number: " + text)
print("Minutes: " + str(minutes))
import java.util.Scanner;

public static void main(String[] args) {
    Scanner scanner
        = new Scanner(System.in);
    int minutes = 5;
    String text = scanner.nextLine();
    try {
        minutes = Integer.parseInt(text);
        System.out.println("Timer set");
    } catch (NumberFormatException e) {
        System.out.println(
            "Not a number: " + text);
    }
    System.out.println(
        "Minutes: " + minutes);
}

The lines that might fail go in the try block. After it comes a block that names one exception and says what to do if it happens: in Python it starts with except and the name, and in Java it starts with catch and the name in brackets. In Java the name is followed by a variable, here e, which holds the exception. You do not have to use it.

If the user types 12, this is printed:

Timer set
Minutes: 12

If the user types twelve, this is printed:

Not a number: twelve
Minutes: 5

Nothing stops. The program reaches its last line either way.

What runs, and what is skipped

Follow the program through twice.

When the conversion works, every line in the try block runs, in order. The block for the exception is skipped completely, and the program carries on after it.

When the conversion fails, the exception is raised at that line, and the program jumps straight to the block that names it. Three things follow from that jump.

  • The line that failed does not finish. The conversion produced no number, so nothing was stored, and minutes still holds the 5 it had before the try.
  • The rest of the try block is skipped. Timer set is not printed, which is right, because the timer was not set from what was typed.
  • After the block for the exception, the program carries on as normal, with the line after it.

So put a line inside the try, after the conversion, when it should only run if the conversion worked. Put a line after the whole try when it should run either way.

Three ways to recover

Catching the exception only stops the crash. You still decide what the program does next. There are three common choices.

  • Use a fallback value, as the timer does. Give the variable a sensible value before the try. If the conversion fails, that value is still there.
  • Skip the bad value and carry on. When a program reads many lines in a loop, a line that cannot be used can be left out, and the loop moves on to the next one.
  • Ask again. Put the reading and the try inside a loop that repeats until a conversion works, so the user can fix their mistake.

Whichever you choose, tell the user what happened. A program that quietly uses 5 minutes when someone typed twelve leaves them wondering why.

Name the exception you expect

The block for the exception only catches the exception it names. If the name does not match, the exception is not caught, and the program stops exactly as if there were no try at all.

In Python, int raises ValueError for text that is not a whole number, so that is the name to catch. In Java, Integer.parseInt throws NumberFormatException, so that is the name to catch.

Remember the question from the last lesson: did the problem come from outside the program? Catch an exception for a value the user typed. Do not catch one to hide a mistake in your own code, such as an index that runs one past the end of a list. Fix that code instead.

Where the two languages differ

In the timer, minutes is given its first value before the try. Here is why that matters in each language.

In Java, a variable made inside the braces of a try block cannot be used after the closing brace. If int minutes were made inside the try, the last print line would be a compile error, error: cannot find symbol, and nothing would run at all.

In Python, a variable first given a value inside the try can be used after it, but only if that line actually ran. If the conversion failed, the variable was never made, and the last print line would stop the program with NameError: name 'minutes' is not defined.

Giving the variable a value before the try avoids both problems.

Two common mistakes

  • Putting a line that should only run on success after the whole try, instead of inside it. It then runs even when the conversion failed.
  • Wrapping several separate conversions in one try. When the first one fails, every line after it in the try is skipped, including the other conversions. Give each value that can fail its own try.

Now try it yourself on the questions below.

Exercise 6.4.16.4.1 · Write itSnack order

Write a program that reads a snack order and prints what it costs. When a line is not a whole number, the program says so and uses a fallback number instead of stopping.

  1. The first line is the number of sandwiches, at 4 dollars each. The second line is the number of drinks, at 2 dollars each.
  2. If the first line is not a whole number, print Sandwiches not clear, using 1 and count 1 sandwich.
  3. If the second line is not a whole number, print Drinks not clear, using 0 and count 0 drinks.
  4. Last, print Total: followed by the cost in dollars.
  5. A line that is a whole number is from 0 to 50, written with digits only. Any other line is a word, a decimal such as 2.5, or empty.

In Python, convert each line with int and catch ValueError. In Java, convert it with Integer.parseInt and catch NumberFormatException.

Your program is run several times. Only what it prints is checked, and spaces at the end of a line are ignored.

Running Java here comes later. You can put this answer's lines in order instead. You can still write your Java answer (it stays while this page is open) and compare it with our worked solution. Or try this exercise in Python.

Examples

Your program is run 10 times: the 3 examples below, and 7 hidden ones that check the rules above. Only what it prints is checked.

  • both lines are whole numbers: 2 times 4 plus 3 times 2 (rules 1 and 4)

    You type

    1. typed: 2
    2. typed: 3

    It prints

    Total: 14
  • the sandwiches line is a word, so 1 sandwich is counted, and the drinks line is still used (rule 2)

    You type

    1. typed: two
    2. typed: 3

    It prints

    Sandwiches not clear, using 1
    Total: 10
  • the drinks line is a word, so 0 drinks are counted (rule 3)

    You type

    1. typed: 2
    2. typed: some

    It prints

    Drinks not clear, using 0
    Total: 8
solution.pySolution.java

Exercise 6.4.26.4.2 · Write itAsk until it is a number

Write a program that reads how many laps a runner ran. It keeps reading lines until one of them is a whole number, instead of stopping at the first line that is not.

  1. For each line that is not a whole number, print Not a whole number: followed by the line, and read the next line.
  2. As soon as a line is a whole number, print Laps: followed by that number. Read no more lines after it.
  3. A line that is a whole number is from 0 to 200, written with digits only. Any other line is a word, a decimal such as 2.5, or empty.
  4. There is always a whole number in the end, but there may be any number of other lines before it.

In Python, convert each line with int and catch ValueError. In Java, convert it with Integer.parseInt and catch NumberFormatException.

Your program is run several times. Only what it prints is checked, and spaces at the end of a line are ignored.

Running Java here comes later. You can put this answer's lines in order instead. You can still write your Java answer (it stays while this page is open) and compare it with our worked solution. Or try this exercise in Python.

Examples

Your program is run 9 times: the 3 examples below, and 6 hidden ones that check the rules above. Only what it prints is checked.

  • the first line is a whole number, so there is nothing to ask again (rule 2)

    You type

    1. typed: 12

    It prints

    Laps: 12
  • one word, then a whole number (rules 1 and 2)

    You type

    1. typed: ten
    2. typed: 10

    It prints

    Not a whole number: ten
    Laps: 10
  • two lines in a row that are not whole numbers (rules 1 and 4)

    You type

    1. typed: ten
    2. typed: 9.5
    3. typed: 9

    It prints

    Not a whole number: ten
    Not a whole number: 9.5
    Laps: 9
solution.pySolution.java