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
hasNextLineguard →NoSuchElementException. - Mixing
nextInt()andnextLine()without consuming leftover newlines—beginners hit this constantly; stick to allnextLine+ 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
-
Logging frameworks (
java.util.logging, Log4j) still ultimately write bytes to files or sockets—the same flush/close concerns apply. -
Build tools copy resources from
src/main/resources; classpath-relative reading usesgetResourceAsStream, a different path model thannew File("x.txt")next to the working directory. -
Unit tests often create temp files with
Files.createTempFilein modern APIs; the discipline of “open, use, close in finally-equivalent” stays identical. -
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.`Scanner` can read from…
2.What is a common risk if you forget to close a file-based `Scanner`/`PrintWriter`?
3.`PrintWriter.println` is useful because…
4.Why might teammates see "mojibake" (garbled characters) in a shared text file?
5.For line-oriented data files, a typical loop pattern is…