Matheion

MA-JAVA0 · Module 13

File I/O and line-based text formats

Topics: Scanner · PrintWriter · File · Encoding

Overview

Session 13 — File I/O and line-based text formats

Many beginner assignments still use plain text files as persistence: one line per record, commas or pipes between fields, maybe a header row. Java’s java.io and java.util.Scanner give you a straightforward path from “file on disk” to “loop over lines in memory,” while PrintWriter is the symmetric tool for writing UTF-16-ish text with familiar println ergonomics.

You will open paths safely with try-with-resources, read until end-of-file without busy-waiting, and think briefly about encoding—why “it worked on my laptop” is not a deployment guarantee.

Time: about 95 minutes.

Concepts

1. Paths and the File class

java.io.File represents a path on the host filesystem—it might or might not point to something that exists. Methods such as exists(), isFile(), length() query metadata without opening a stream.

import java.io.File;

File f = new File("ledger.txt");
System.out.println(f.getAbsolutePath());

For new code, java.nio.file.Path and Files are often preferred, but introductory courses still lean on File + Scanner + PrintWriter; learn this stack first.


2. Reading lines with Scanner

Scanner can read from System.in, a String, or a File. Token-based methods (next(), nextInt()) split on whitespace; nextLine() returns the remainder of the current line including spaces (minus the line terminator).

try (Scanner in = new Scanner(new File("data.txt"))) {
    while (in.hasNextLine()) {
        String line = in.nextLine();
        // process line
    }
}

Loop until hasNextLine() is false—do not assume a sentinel blank line exists.


3. Writing with PrintWriter

PrintWriter offers print, println, printf. Construct with a filename string or a File. Unchecked FileNotFoundException may surface from the constructor when the path is not creatable—often you let it propagate or wrap.

try (PrintWriter out = new PrintWriter("out.txt")) {
    out.println("header:v1");
    out.println("row:example");
}

try-with-resources guarantees close runs—flushing buffered characters—so partial writes still leave a consistent file boundary when possible.


4. Checked IOException discipline

Many I/O constructors and methods declare throws IOException. Your choices: catch and recover, or add throws IOException to your own method so callers participate in the contract. Mixing silent failure with I/O is how homework “sometimes works.”


5. Encoding in one paragraph

Text files are bytes; charset rules map bytes ↔ characters. Scanner and PrintWriter default to the platform encoding unless you specify one—fine for local classwork, risky when teammates use different OS defaults. Awareness beats surprise: if output looks mojibake, charset mismatch is a prime suspect.


6. Line-based formats

Design simple invariants: one record per line, fixed delimiter count, optional header starting with #. Validate early (split length, numeric shape) and throw meaningful exceptions rather than letting parseInt blow up deep inside unrelated code.


7. Pitfalls

  • Forgetting hasNextLine guard → NoSuchElementException.
  • Mixing nextInt() and nextLine() without consuming leftover newlines—beginners hit this constantly; stick to all nextLine + manual parsing until confident.
  • Opening the same file for read and write without closing the reader first on some OSes—close or use try-with-resources in nested scopes.

Examples

Example A — copy lines with transformation

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Scanner;

public class UppercaseCopy {
    public static void copyUpper(String inPath, String outPath) throws IOException {
        try (Scanner in = new Scanner(new File(inPath));
             PrintWriter out = new PrintWriter(outPath)) {
            while (in.hasNextLine()) {
                String line = in.nextLine();
                out.println(line.toUpperCase());
            }
        }
    }
}

Two resources in one try-with-resources header: closes occur in reverse order of declaration—writer then reader—after the block exits.


Example B — skip blank lines

while (in.hasNextLine()) {
    String line = in.nextLine().trim();
    if (line.isEmpty()) {
        continue;
    }
    if (line.startsWith("#")) {
        continue; // comment
    }
    // parse structured line here
}

Trimming before isEmpty removes lines that are only spaces or tabs—common in hand-edited files.

Bridge

I/O beyond toy programs

  1. Logging frameworks (java.util.logging, Log4j) still ultimately write bytes to files or sockets—the same flush/close concerns apply.

  2. Build tools copy resources from src/main/resources; classpath-relative reading uses getResourceAsStream, a different path model than new File("x.txt") next to the working directory.

  3. Unit tests often create temp files with Files.createTempFile in modern APIs; the discipline of “open, use, close in finally-equivalent” stays identical.

  4. Containers treat the filesystem as ephemeral—your first serious apps move persistence to databases, but text I/O remains the pedagogical bridge.

When you can narrate the lifecycle of a Scanner tied to a File, you are ready for larger persistence layers.

Practice check — file I/O and line-based text

Multiple-choice check from the Matheion app lesson. Pick an answer for each question, then submit.

  1. 1.`Scanner` can read from…

  2. 2.What is a common risk if you forget to close a file-based `Scanner`/`PrintWriter`?

  3. 3.`PrintWriter.println` is useful because…

  4. 4.Why might teammates see "mojibake" (garbled characters) in a shared text file?

  5. 5.For line-oriented data files, a typical loop pattern is…