Room 7 of 15 · about 25 minutes

Opening a file with Scanner

Rooms 1 through 5 filled arrays by hand and room 6 gave you the line the compiler asks for. Real input comes from a file, and this room opens one.

Tasks checked0 of 4 XP earned on this path0

What this room checks you can do

Student can construct a Scanner over a file using the two-step idiom new Scanner(new File(filename)), declare or catch the FileNotFoundException the constructor throws, and close the Scanner to release the file handle when finished.

Notes

File as path, not content

The java.io.File class is one of the most misnamed classes in the JDK. A File object does not hold the contents of a file: it holds the file's path, the string that names where a file might be on disk. new File("hamlet.txt") performs zero disk I/O; it just stores the string "hamlet.txt" inside a Java object. The disk is touched only when something else (a Scanner, a PrintStream, a method like exists()) asks the operating system about that path.

This separation is deliberate. A path can refer to a file that does not exist yet (you might be about to create it), a file you do not have permission to read, a directory rather than a file, or a perfectly normal readable file. The File class lets you build the reference, query the disk to find out which case you are in, and pass the reference to other classes that will actually open it.

import java.io.*;

final File f = new File("hamlet.txt");   // No disk I/O. Just a path object.
System.out.println(f.exists());           // Now we ask the OS: true or false?
System.out.println(f.getAbsolutePath());  // String formatting only: still no read.

In other languages

  • Python: pathlib.Path("hamlet.txt") is the direct analog: a path object you can query before opening.
  • C: there is no path object; you pass a const char * filename directly to fopen. stat() and access() do what File.exists()/File.canRead() do.

Constructing a Scanner from a File

A Scanner is a tokenizer plus parser: it consumes a stream of characters and hands back chunks (next()), lines (nextLine()), or typed values (nextInt(), nextDouble()). To read from disk instead of the keyboard, swap the source: pass a File to the Scanner constructor.

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

public static int countLines(final String filename) throws FileNotFoundException {
    final File f = new File(filename);          // step 1: build the path object
    final Scanner sc = new Scanner(f);          // step 2: open the file for reading
    int n = 0;
    while (sc.hasNextLine()) {
        sc.nextLine();
        n++;
    }
    sc.close();                                 // step 3: release the file handle
    return n;
}

Three things make this idiom different from the keyboard Scanner students wrote in earlier weeks:

1. The two-step construction. new File(filename) does not touch the disk; it only stores the path as an object. The disk read happens when the Scanner constructor actually opens the file. The two-step shape (new Scanner(new File(...))) is the convention because the File object also lets you call exists(), isFile(), canRead(), and other validation methods before opening.

2. You must close() what you open. Unlike the keyboard Scanner (which most programs intentionally leave open for the lifetime of the program), a file Scanner holds an OS file handle. Forgetting to close means the OS cannot reclaim it, and on some platforms it blocks other programs from accessing the file. Always close as soon as you have finished reading. (Week 7's "try-with-resources" introduces a syntax that closes for you, but the explicit close() is the foundation.)

3. The constructor is declared to throw FileNotFoundException. Every method that opens a file must either catch it or declare it on the header. The rule that FileNotFoundException is checked covers what that means.

What if I need two passes over the file?

Open a fresh Scanner each time. A Scanner does not have a rewind operation: once you have read past a line, you cannot go back without constructing a new Scanner on a new (or the same) File. The count-allocate-fill pattern, where pass 1 counts the values to learn how big an array to allocate and pass 2 fills the array, does exactly this: two Scanner objects, each closed after its pass.

In other languages

  • Python: open(filename, "r") returns a file object; iterating with for line in f: is the analog of while (sc.hasNextLine()).
  • C: FILE *fp = fopen(filename, "r") followed by fgets or fscanf. fclose is the close() equivalent.
  • JavaScript (Node): fs.readFileSync returns the whole content as a string; you then split or scan it yourself. There is no built-in lazy tokenizer like Scanner.

What this room assumes you already have

Tasks

Do each one, then check the box. Checking a box is you saying you did it. You can uncheck a box if you check it by accident.

  1. trace
    Show the answer

    no exception: only the path string is stored. The error would only appear when something tries to actually read or write the file.

  2. write
    Show the answer

    final File f = new File("data/scores.txt"); System.out.println(f.getAbsolutePath());.

  3. write
    Show the answer

    open a Scanner on a File, return sc.nextLine() after closing, declare throws FileNotFoundException.

  4. trace
    Show the answer

    an OS file handle (the JVM eventually reclaims it on garbage collection, but timing is not guaranteed and on some platforms the file remains locked).

Self check

Type what you think the answer is. Getting it wrong costs nothing and you can try as many times as you want.

Given new File("data.txt") on a system where data.txt does not exist, predict whether the constructor throws.

Practice, untimed

Open this whenever you want, before the tasks or after them. Nothing in this section is recorded and nothing here is timed.

  1. Given File f = new File("hamlet.txt"); System.out.println(f);, predict the printed output.trace
    Show the answer

    hamlet.txt (the path string, via File.toString()): not the file's contents.

  2. Given a sequence of two calls new Scanner(new File("a.txt")) and new Scanner(new File("a.txt")) after the first is closed, predict whether the second works.trace
    Show the answer

    yes: closing one Scanner does not delete or lock the file.

Optional challenge

This one is optional. Do what the room says you can do, without opening any answers, then read the two traps below and check your work against them. Each trap is copied from the notes for this room.

Student can construct a Scanner over a file using the two-step idiom new Scanner(new File(filename)), declare or catch the FileNotFoundException the constructor throws, and close the Scanner to release the file handle when finished.

How this room finishes

This room is done when all four tasks are checked and the self check is answered.