Using Text Files
Why this matters
Imagine you've just spent hours building a game. Your friend, Priya, plays it and gets the highest score ever! You save her score in a variable. But then, you close the program. When you open it again... her score is gone. All the data stored in your program's memory vanishes the moment it stops running.
This is a huge problem for any real-world application, from video games to banking apps. We need a way to save data permanently. This is where files come in. A file is like a digital notebook for your program. It can write information down and, just as importantly, read it back later.
In this lesson, we'll focus on that second part: teaching your Java program how to open a text file, read the data inside it, and use that data to do something useful.
Concept overview
flowchart TD
A[Start] --> B{Create File object<br/>("data.txt")};
B --> C{Create Scanner object<br/>(using File object)};
C --> D{File exists?};
D -- No --> E[Program throws<br/>FileNotFoundException];
D -- Yes --> F{scanner.hasNext()?};
F -- Yes --> G[Read data<br/>e.g., scanner.next()];
G --> H[Process data];
H --> F;
F -- No --> I[scanner.close()];
I --> J[End];
Core explanation
Why Files? The Whiteboard vs. The Notebook
Think of your program's memory (the RAM) as a giant whiteboard. While the program is running, you can write on it, erase things, and read what's there. It's fast and useful. But as soon as the power is cut—as soon as your program ends—an eraser wipes the entire board clean.
A file is like a notebook. The information you write in it stays there, even after you put the notebook away. This is called persistence. When your program needs to remember something for later, like a high score, a user's saved game, or a list of contacts, it writes that data to a file.
Our focus today is on reading data that's already in a file.
The Three Key Players for Reading Files
To read a file in Java, you need to know about three things from the java.io package:
File: This object doesn't hold the file's data. Instead, it represents the file itself. Think of it as a signpost that has the name and location of the file, like"scores.txt".Scanner: This is the real workhorse. You give aScanneraFileobject, and it does the actual reading. It has methods to grab numbers, words, or entire lines from the file.IOException: This is an "exception," or a type of error. What if you try to read a file named"scores.txt"but it doesn't exist? Your program would crash. Java requires you to plan for this possibility.IOExceptionstands for "Input/Output Exception," and it's the error that gets thrown when file operations go wrong.
Setting Up Your Program to Read a File
Let's look at the basic structure. Imagine we have a file named data.txt.
First, you need to import the necessary classes at the top of your Java file.
import java.io.File; // For the File class
import java.io.IOException; // For the error handling
import java.util.Scanner; // For the Scanner class
Next, in any method that is going to handle files, you must tell Java you're prepared for a file-related error. You do this by adding throws IOException to the method signature.
public static void main(String[] args) throws IOException {
// Our file-reading code will go here
}
This is where a lot of students slip up. They forget throws IOException and their code won't even compile. Think of it as a required safety warning, like "Caution: Contents May Be Hot." You're telling Java, "I know a file might not be found, and I'm prepared for that possibility." For now, it just means the program will stop if the file is missing.
Opening and Reading the File
Inside your method, you'll create the File and Scanner objects.
// 1. Create a File object that points to your file.
File dataFile = new File("data.txt");
// 2. Create a Scanner that uses the File object.
Scanner fileScanner = new Scanner(dataFile);
Now fileScanner is connected to data.txt and ready to read! But how do we read everything if we don't know how long the file is? We use a while loop with the hasNext() method.
The hasNext() method is like asking the Scanner, "Is there anything left to read?" It returns true if there is, and false if you've reached the end of the file.
// 3. Loop as long as the file has more content.
while (fileScanner.hasNext()) {
// Read and process the data inside the loop
String word = fileScanner.next();
System.out.println(word);
}
The Scanner's Toolbox
The Scanner has different methods depending on what kind of data you expect to read.
next(): Reads the next "token" (a sequence of characters separated by spaces) as aString.nextInt(): Reads the next token and tries to convert it to anint.nextDouble(): Reads the next token and tries to convert it to adouble.nextLine(): Reads all remaining characters on the current line, including spaces, until it hits the newline character (from pressing Enter).
The nextLine() Trap: A Critical Warning
This is one of the most common and frustrating bugs you'll encounter.
Imagine a file player.txt:
Aaliyah
95.5
12
You write this code:
// Reads the name "Aaliyah"
String name = fileScanner.nextLine();
// Reads the score 95.5
double score = fileScanner.nextDouble();
// Reads the level 12
int level = fileScanner.nextInt();
This seems fine. But what if the file was formatted differently?
player_info.txt:
12 95.5
Aaliyah Johnson
And you try to read it like this:
int level = fileScanner.nextInt(); // Reads 12
double score = fileScanner.nextDouble(); // Reads 95.5
String name = fileScanner.nextLine(); // Tries to read "Aaliyah Johnson"
The name variable will be an empty string! Why?
When fileScanner.nextDouble() reads 95.5, it leaves the newline character (the invisible "Enter" key press) in the input stream. When fileScanner.nextLine() is called, it sees that leftover newline character immediately and thinks, "Ah, an empty line!" and stops reading.
The fix: After you read a number with nextInt() or nextDouble(), if you plan to read a whole line next with nextLine(), you must add an extra fileScanner.nextLine(); to consume the leftover newline character.
int level = fileScanner.nextInt();
double score = fileScanner.nextDouble();
fileScanner.nextLine(); // <-- The crucial fix! Consumes the leftover newline.
String name = fileScanner.nextLine(); // Now this works correctly.
Splitting Lines for Complex Data
What if a single line in your file contains multiple pieces of information, separated by commas? This is a very common format, called CSV (Comma-Separated Values).
teams.txt:
Red Sox,Boston,92
Yankees,New York,99
Reading this with next() would be messy. A better way is to read the entire line with nextLine() and then use the String.split() method.
split(del) takes a "delimiter" (the character you want to split by) and returns a String array.
while (fileScanner.hasNextLine()) {
String line = fileScanner.nextLine(); // "Red Sox,Boston,92"
String[] parts = line.split(","); // parts is now ["Red Sox", "Boston", "92"]
String teamName = parts[0];
String city = parts[1];
int wins = Integer.parseInt(parts[2]); // Don't forget to parse numbers!
System.out.println(teamName + " from " + city + " had " + wins + " wins.");
}
Don't Forget to Clean Up!
Once your while loop is finished and you're done with the file, you must close the scanner. This releases the file from your program's control. It's good practice and prevents potential issues.
// After the loop...
fileScanner.close(); // 4. Close the scanner to release the file.
System.out.println("File reading complete.");
Think of it as closing a book and putting it back on the shelf.
See it in action
Worked examples
Calculating the Average Score
Let's say we have a file named scores.txt containing a list of exam scores.
scores.txt:
88.5
92
76.5
100
83
Problem: Write a program to read all the scores from scores.txt, calculate their average, and print the result.
Solution Walkthrough:
- 1SetupWe need to import
File,IOException, andScanner. Ourmainmethod must includethrows IOException. - 2InitializationWe'll create
FileandScannerobjects. We also need variables to keep track of the total sum and the count of scores. - 3The LoopWe'll use a
whileloop withfileScanner.hasNextDouble()to make sure we only readdoublevalues. Inside the loop, we'll read each score usingfileScanner.nextDouble(), add it to oursum, and increment ourcount. - 4Calculation & CleanupAfter the loop finishes, we'll calculate the average (
sum / count). Finally, we'll close the scanner and print the result.
Code:
import java.io.File;
import java.io.IOException;
import java.util.Scanner;
public class ScoreCalculator {
public static void main(String[] args) throws IOException {
File scoreFile = new File("scores.txt");
Scanner fileScanner = new Scanner(scoreFile);
double sum = 0.0;
int count = 0;
while (fileScanner.hasNextDouble()) {
double score = fileScanner.nextDouble();
sum += score;
count++;
}
fileScanner.close(); // Close the scanner!
if (count > 0) {
double average = sum / count;
System.out.printf("The average score is: %.2f\n", average);
} else {
System.out.println("No scores found in the file.");
}
}
}
Example 2: Processing a Roster with split()
Now for a more complex file, roster.csv, containing player data.
roster.csv:
Carlos,Rodon,10
Jordan,Montgomery,52
Marcus,Stroman,0
Problem: Read the roster, and for each player, print their full name and jersey number in a user-friendly format.
Solution Walkthrough:
- 1SetupSame as before, with
throws IOException. - 2The LoopSince each line is a complete record, we'll use
while (fileScanner.hasNextLine()). This is better thanhasNext()because we want to process the data line by line. - 3Read and SplitInside the loop, we'll read the entire line into a
Stringvariable usingfileScanner.nextLine(). Then, we'll useline.split(",")to break the line into aStringarray. - 4Process and PrintWe'll access the parts of the array (
parts[0],parts[1],parts[2]) to get the first name, last name, and number. Then we'll print a formatted string. - 5CleanupClose the scanner after the loop.
Code:
import java.io.File;
import java.io.IOException;
import java.util.Scanner;
public class RosterReader {
public static void main(String[] args) throws IOException {
File rosterFile = new File("roster.csv");
Scanner fileScanner = new Scanner(rosterFile);
System.out.println("Team Roster:");
while (fileScanner.hasNextLine()) {
String line = fileScanner.nextLine();
String[] parts = line.split(",");
String firstName = parts[0];
String lastName = parts[1];
String number = parts[2]; // It's a String!
System.out.println("- #" + number + " " + firstName + " " + lastName);
}
fileScanner.close();
}
}
Try it yourself
Ready to try it yourself? Create the text files described below and write the Java code to solve the problems.
1. City Filter
Create a file named cities.txt with the following content:
Seattle
Boston
San Diego
Chicago
Atlanta
Santa Fe
Write a program that reads cities.txt and prints only the cities that start with the letter "S".
2. Student Averages
Create a file named grades.txt with student names and three test scores, separated by commas.
Liam Smith,85,90,95
Sofia Garcia,100,92,94
Maya Patel,78,82,80
Write a program that reads grades.txt, and for each student, calculates and prints their name and average test score.
Practice — 8 questions
In simple terms, using text files is about teaching your Java program how to read and use information stored in separate files, just like you'd read a grocery list or a list of high scores.
import java.io.File; // For the File class
import java.io.IOException; // For the error handling
import java.util.Scanner; // For the Scanner class
- 4.6.A: Develop code to read data from a text file.
- 4.6.A.1
- A file is storage for data that persists when the program is not running. The data in a file can be retrieved during program execution.
- 4.6.A.2
- A file can be connected to the program using the File and Scanner classes.
- 4.6.A.3
- A file can be opened by creating a File object, using the name of the file as the argument of the constructor. • File(String str) is the File constructor that accepts a String file name to open for reading, where str is the pathname for the file.
- 4.6.A.4
- When using the File class, it is required to indicate what to do if the file with the provided name cannot be opened. One way to accomplish this is to add throws IOException to the header of the method that uses the file. If the file name is invalid, the program will terminate.
- 4.6.A.5
- The File and IOException classes are part of the java.io package. An import statement must be used to make these classes available for use in the program.
- 4.6.A.6
- The following Scanner methods and constructor—including what they do and when they are used—are part of the Java Quick Reference: • Scanner(File f) is the Scanner constructor that accepts a File for reading. • int nextInt() returns the next int read from the file or input source if available. If the next int does not exist or is out of range, it will result in an InputMismatchException. • double nextDouble() returns the next double read from the file or input source. If the next double does not exist, it will result in an InputMismatchException. • boolean nextBoolean() returns the next boolean read from the file or input source. If the next boolean does not exist, it will result in an InputMismatchException. • String nextLine() returns the next line of text as a String read from the file or input source; can return the empty string if called immediately after another Scanner method that is reading from the file or input source. • String next() returns the next String read from the file or input source. • boolean hasNext() returns true if there is a next item to read in the file or input source; returns false otherwise. • void close() closes this scanner.
- 4.6.A.7
- Using nextLine and the other Scanner methods together on the same input source sometimes requires code to adjust for the methods' different ways of handling whitespace.
- 4.6.A.8
- The following additional String method— including what it does and when it is used—is part of the Java Quick Reference: • String[] split(String del) returns a String array where each element is a substring of this String, which has been split around matches of the given expression del.
- 4.6.A.9
- A while loop can be used to detect if the file still contains elements to read by using the hasNext method as the condition of the loop.
- 4.6.A.10
- A file should be closed when the program is finished using it. The close method from Scanner is called to close the file.
flowchart TD
A[Start] --> B{Create File object<br/>("data.txt")};
B --> C{Create Scanner object<br/>(using File object)};
C --> D{File exists?};
D -- No --> E[Program throws<br/>FileNotFoundException];
D -- Yes --> F{scanner.hasNext()?};
F -- Yes --> G[Read data<br/>e.g., scanner.next()];
G --> H[Process data];
H --> F;
F -- No --> I[scanner.close()];
I --> J[End];
Read what Saavi narrates
Hello, and welcome to Shrutam! I'm Saavi, and today we're going to talk about one of the most practical skills in programming: reading data from files.
Imagine you've built a game and your friend Priya gets the high score. You save it in a variable, but when you close the program and open it again... her score is gone. That's because program memory is temporary. To save data permanently, we need to use files. Today, we'll learn how to teach our Java programs to read information that's already been saved in a text file.
It’s a two-step process. First, we have to tell our program where the file is. Then, we use a special tool to read what's inside it.
Let's walk through an example. Imagine we have a file called scores dot t-x-t, and it's just a list of numbers, like eighty-eight point five, ninety-two, and so on. Our goal is to read these scores and calculate the average.
First, in our code, we need to create a File object that points to 'scores.txt'. Then, we create a Scanner object that will use that file.
We also need a couple of variables... one to hold the sum of the scores, and one to count how many scores we've read. We'll start them both at zero.
Now for the main part. We'll use a while loop. The condition for the loop will be... as long as the scanner has another number to read. Inside the loop, we'll read the number, add it to our sum, and add one to our count. The loop repeats until it runs out of numbers in the file.
After the loop finishes, we have the total sum and the total count. We can calculate the average by dividing the sum by the count. And finally, we print our result! The average score is... whatever we calculated.
One last, very important step: we have to close the scanner. This tells the operating system we're done with the file. It's like closing a book and putting it back on the shelf.
A common mistake here is dealing with files that have both numbers and text. If you read a number, and then try to read a line of text right after, you can get a weird bug where it skips the text. The fix is to add an extra call to the scanner's nextLine method, just to clear out any leftover characters from when you read the number. It's a tricky one, but once you know the pattern, you'll be able to spot and fix it every time.
Reading files opens up a whole new world of possibilities for your programs. Keep practicing, and you'll get the hang of it in no time.
Java considers a missing file a "checked exception," meaning you are *required* to handle it or declare that your method might throw it. Your code will not compile without it.
Always add `throws IOException` to the header of any method that creates a `Scanner` from a `File`. Example: `public static void main(String[] args) throws IOException`.
The `nextInt()` and `nextDouble()` methods leave the newline character (`\n`) in the input buffer. The subsequent `nextLine()` call reads this invisible character as an empty line, skipping the data you actually wanted.
After calling `nextInt()` or `nextDouble()`, add a "dummy" `scanner.nextLine();` call to consume the leftover newline character before you try to read the next full line of text.
This can lead to resource leaks. The operating system has a limit on how many files can be open at once. In a large application, failing to close files can cause the program to fail unexpectedly. It can also sometimes prevent other programs (or even your own) from accessing the file.
Make it a habit. As soon as your file-reading loop is done, write `scanner.close();`.
Your code has a correct file name, but the program can't find the file. This is usually a pathing issue, not a code issue.
In most simple IDE setups (like BlueJ, DrJava, or a basic VS Code project), place your `.txt` file in the top-level folder of your project, right next to your `src` folder, not inside it.
`next()` reads only up to the first space. If your file contains a line like "San Francisco", `scanner.next()` will only return "San". The next call to `scanner.next()` will return "Francisco".
If you need to read a piece of text that includes spaces (like a name, a city, or a sentence), use `scanner.nextLine()` to read the entire line at once.