File Processing
IB Syllabus: B2.5 — File processing
Table of Contents
- Why Files?
- Reading a File
- Writing to a File
- Appending to a File
- Reading with BufferedReader
- Writing with BufferedWriter
- Exception Handling in File Processing
- A Complete Example: Student Grade Logger
- Summary
- Quick Check
- Practice Exercises
Why Files?
Every program so far has used Scanner to read from the keyboard and System.out.println to write to the screen. When the program ends, all data disappears.
Files allow programs to persist data — to read information that was stored by a previous run, and to save results that will outlast the current session. A student record system, a game’s high score list, or a bank’s transaction log all need file processing.
Picture it: A file called
scores.txtsits on the hard drive with 30 lines of student names and scores. How does your program reach that data? The answer is the same Scanner you already know — just pointed at a file instead of the keyboard.
Reading a File
Java’s Scanner class can read from a file just as easily as from the keyboard — the only difference is what you pass to its constructor.
import java.util.Scanner;
import java.io.File;
import java.io.FileNotFoundException;
public class ReadFile {
public static void main(String[] args) {
try {
File file = new File("scores.txt");
Scanner reader = new Scanner(file);
while (reader.hasNextLine()) {
String line = reader.nextLine();
System.out.println(line);
}
reader.close();
} catch (FileNotFoundException e) {
System.out.println("Error: file not found.");
}
}
}
Assume scores.txt contains:
Alice 85
Bob 72
Charlie 91
Output:
Alice 85
Bob 72
Charlie 91
Reading and Processing Values
You can parse the data from each line rather than just printing it:
try {
Scanner reader = new Scanner(new File("scores.txt"));
int total = 0;
int count = 0;
while (reader.hasNextLine()) {
String line = reader.nextLine();
Scanner lineScanner = new Scanner(line);
String name = lineScanner.next(); // read the name
int score = lineScanner.nextInt(); // read the score
System.out.println(name + " scored " + score);
total += score;
count++;
}
reader.close();
System.out.println("Average: " + (double) total / count);
} catch (FileNotFoundException e) {
System.out.println("File not found.");
}
reader.hasNextLine()returnstrueas long as there is another line to read. This is the standard loop condition for reading files of unknown length.
Think about it: If you open a text file and start typing, what happens to the existing content? What if you wanted to add to the end without replacing it? There are two different modes for exactly these two cases.
Writing to a File
FileWriter writes text to a file. By default, it overwrites any existing content.
import java.io.FileWriter;
import java.io.IOException;
public class WriteFile {
public static void main(String[] args) {
try {
FileWriter writer = new FileWriter("output.txt");
writer.write("Alice,85\n");
writer.write("Bob,72\n");
writer.write("Charlie,91\n");
writer.close();
System.out.println("File written successfully.");
} catch (IOException e) {
System.out.println("Error writing file: " + e.getMessage());
}
}
}
Always call
writer.close(). Failing to close a file may result in data not being flushed to disk — some content could be lost.
Appending to a File
To add content to the end of an existing file without overwriting it, pass true as the second argument to FileWriter.
try {
FileWriter writer = new FileWriter("log.txt", true); // true = append mode
writer.write("New entry added to log.\n");
writer.close();
} catch (IOException e) {
System.out.println("Error: " + e.getMessage());
}
| Mode | Constructor | Behaviour |
|---|---|---|
| Overwrite | new FileWriter("file.txt") | Clears existing content, starts fresh |
| Append | new FileWriter("file.txt", true) | Adds to end of existing content |
Reading with BufferedReader
BufferedReader is more efficient for reading large files because it reads data in chunks (a buffer) rather than one character at a time.
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class BufferedReadFile {
public static void main(String[] args) {
try {
BufferedReader reader = new BufferedReader(new FileReader("scores.txt"));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
reader.close();
} catch (IOException e) {
System.out.println("Error reading file: " + e.getMessage());
}
}
}
The condition (line = reader.readLine()) != null does two things at once:
- Reads the next line and assigns it to
line - Checks if
readLine()returnednull(which signals end-of-file)
| Class | Used for | Efficiency |
|---|---|---|
Scanner(File) | Small files, mixed types (int, String) | Lower |
BufferedReader | Line-by-line text, larger files | Higher |
Writing with BufferedWriter
For writing, BufferedWriter wraps FileWriter to buffer output and add the newLine() convenience method.
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class BufferedWriteFile {
public static void main(String[] args) {
try {
BufferedWriter writer = new BufferedWriter(new FileWriter("results.txt"));
writer.write("Name,Score");
writer.newLine();
writer.write("Alice,85");
writer.newLine();
writer.write("Bob,72");
writer.newLine();
writer.close();
} catch (IOException e) {
System.out.println("Error: " + e.getMessage());
}
}
}
writer.newLine() inserts the correct line separator for the operating system (Windows uses \r\n, macOS/Linux use \n). This is better practice than hardcoding "\n".
Before reading: List every way a file operation could fail. The exception handling pattern below covers each one — match them up as you read.
Exception Handling in File Processing
File operations can fail in predictable ways:
- The file does not exist (
FileNotFoundException) - The directory has no write permission (
IOException) - The file is already in use by another process
Always wrap file operations in try/catch/finally. The finally block ensures the file is closed even if an exception occurs.
import java.io.*;
public class SafeFileRead {
public static void main(String[] args) {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader("data.txt"));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (FileNotFoundException e) {
System.out.println("File not found: " + e.getMessage());
} catch (IOException e) {
System.out.println("Read error: " + e.getMessage());
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
System.out.println("Could not close file.");
}
}
}
}
}
The reader is declared outside the
tryblock so it is accessible infinally. Inside thetry, it is opened. Thefinallyblock closes it regardless of what happened. This pattern prevents resource leaks.
A Complete Example: Student Grade Logger
This program reads existing scores from a file, calculates the class average, and appends a new score entered by the user.
import java.util.Scanner;
import java.io.*;
public class GradeLogger {
public static void main(String[] args) {
String filename = "grades.txt";
// Step 1: Read and display existing scores
System.out.println("--- Current Scores ---");
try {
BufferedReader reader = new BufferedReader(new FileReader(filename));
String line;
int total = 0;
int count = 0;
while ((line = reader.readLine()) != null) {
System.out.println(line);
total += Integer.parseInt(line.trim());
count++;
}
reader.close();
if (count > 0) {
System.out.println("Average: " + (double) total / count);
}
} catch (FileNotFoundException e) {
System.out.println("(No existing file — starting fresh.)");
} catch (IOException e) {
System.out.println("Error reading file.");
}
// Step 2: Add a new score
Scanner input = new Scanner(System.in);
System.out.print("\nEnter new score to add: ");
int newScore = input.nextInt();
try {
FileWriter writer = new FileWriter(filename, true); // append
writer.write(newScore + "\n");
writer.close();
System.out.println("Score saved.");
} catch (IOException e) {
System.out.println("Error saving score.");
}
}
}
Summary
| Task | Classes to use |
|---|---|
| Read a small file, mixed types | Scanner(new File(...)) |
| Read a large text file line-by-line | BufferedReader(new FileReader(...)) |
| Write to a file (overwrite) | FileWriter("file.txt") |
| Append to a file | FileWriter("file.txt", true) |
| Write efficiently with newlines | BufferedWriter(new FileWriter(...)) |
| Always | Wrap in try/catch, close in finally |
Quick Check
Q1. new FileWriter("log.txt") — what happens to any content already in log.txt?
Q2. Which constructor appends to a file rather than overwriting it?
Q3. What does reader.hasNextLine() return when there are no more lines?
Q4. Why is the finally block important when working with files?
Q5. Why is BufferedReader preferred over Scanner for reading large files?
Practice Exercises
- Write a program that reads integers from a file called
numbers.txt(one per line) and prints the sum and count. - Write a program that asks the user for five names and writes them to a file called
names.txt, one per line. - Modify exercise 2 to append (not overwrite) each time the program runs. Run it twice and verify the file grows.
- Write a program that copies all lines from
input.txttooutput.txt, converting each line to uppercase. - Challenge: Write a program that reads a CSV file where each line is
name,score(e.g.Alice,85). Parse each line, calculate the class average, and write a summary to a new filesummary.txtthat lists all names with their scores and the overall average at the bottom.