Learn Java

How to Read a File in Java, with Examples

By Updated 8 min read

In Java 11 and later, the simplest way to read a text file is Files.readString(Path.of("notes.txt")), which gives you the whole file as one String, or Files.readAllLines(Path.of("notes.txt")), which gives you a List<String> with one element per line. Every example on this page runs right here on a small sample file, so you can press Run and step through it. After the short way come the classic ways, BufferedReader and Scanner, then what to do when the file is missing and a table of which way to use.

Main.java
1import java.io.IOException;
2import java.nio.file.Files;
3import java.nio.file.Path;
4import java.util.List;
5
6public class Main {
7 public static void main(String[] args) throws IOException {
8 Path file = Path.of("notes.txt");
9
10 String text = Files.readString(file);
11 System.out.print(text);
12
13 List<String> lines = Files.readAllLines(file);
14 System.out.println(lines);
15 System.out.println("The file has " + lines.size() + " lines.");
16 }
17}
Files before the program runs
notes.txt
Buy milk
Walk the dog
Call Maya
Prints
›Buy milk
›Walk the dog
›Call Maya
›[Buy milk, Walk the dog, Call Maya]
›The file has 3 lines.

Path.of turns the file name into a Path, Java's object for where a file is, and Files is a class of static methods that work on paths. readString keeps the line breaks inside the text, so System.out.print, with no ln, still prints three lines. readAllLines cuts the text at the line breaks and leaves them out, so lines prints as [Buy milk, Walk the dog, Call Maya]. Both methods open the file, read all of it and close it again before they return.

The throws IOException on main is there because reading a file can fail; the section on a missing file explains it. readString arrived in Java 11. On Java 8, new String(Files.readAllBytes(path), StandardCharsets.UTF_8) does the same job.

Both load the whole file
readString and readAllLines hold the entire file in memory at once. That is fine for notes or a CSV of a few thousand lines. For a file of hundreds of megabytes, read a line at a time instead, as the next section does. readString throws an OutOfMemoryError on a file larger than about 2 GB.

Read a file line by line with BufferedReader#

A BufferedReader reads a file one line at a time, so only a small part of the file is in memory at once, however big the file is. It is the classic way to read a text file and works on every Java version. You build it around a FileReader, the object that opens the file:

Main.java
1import java.io.BufferedReader;
2import java.io.FileReader;
3import java.io.IOException;
4
5public class Main {
6 public static void main(String[] args) throws IOException {
7 try (BufferedReader reader = new BufferedReader(new FileReader("notes.txt"))) {
8 String line;
9 int number = 1;
10 while ((line = reader.readLine()) != null) {
11 System.out.println(number + ": " + line);
12 number++;
13 }
14 }
15 }
16}
Files before the program runs
notes.txt
Buy milk
Walk the dog
Call Maya
Prints
›1: Buy milk
›2: Walk the dog
›3: Call Maya

Each call to readLine() returns the next line without its line break, and returns null once the file has no lines left. The condition (line = reader.readLine()) != null reads a line, stores it in line and checks it in one go, so the loop body runs once per line and the loop stops at the end of the file. Step through it and watch line take each value, then null.

The reader is declared in the parentheses after try. That form is called try-with-resources, and it closes the file when the block ends, even if an exception ends it early. Files.newBufferedReader(Path.of("notes.txt")) gives you the same kind of reader from a Path.

Every call to readLine() moves on to the next line, and that makes one mistake very common. Decide what this loop prints before you run it:

Predict the outputMain.java
1import java.io.BufferedReader;
2import java.io.FileReader;
3import java.io.IOException;
4import java.nio.file.Files;
5import java.nio.file.Path;
6
7public class Main {
8 public static void main(String[] args) throws IOException {
9 Files.writeString(Path.of("notes.txt"), "Buy milk\nWalk the dog\nCall Maya\n");
10
11 try (BufferedReader reader = new BufferedReader(new FileReader("notes.txt"))) {
12 while (reader.readLine() != null) {
13 System.out.println(reader.readLine());
14 }
15 }
16 }
17}

The first line of main writes the same three lines to notes.txt. The loop calls readLine() twice on each pass. What does it print?

It prints Walk the dog and then null. The condition reads Buy milk and throws it away, and the body prints the line after it. On the second pass the condition uses up Call Maya, so the body finds the end of the file and prints null. Read each line once, into a variable, as the loop above does.

Line by line as a stream: Files.lines#

Files.lines(path) also reads one line at a time, and hands the lines over as a Stream<String>, a sequence you can filter and count with stream methods. This program prints the lines of a server log that start with ERROR:

Main.java
1import java.io.IOException;
2import java.nio.file.Files;
3import java.nio.file.Path;
4import java.util.stream.Stream;
5
6public class Main {
7 public static void main(String[] args) throws IOException {
8 try (Stream<String> lines = Files.lines(Path.of("server.log"))) {
9 lines.filter(line -> line.startsWith("ERROR"))
10 .forEach(System.out::println);
11 }
12 }
13}
Files before the program runs
server.log
INFO  server started
INFO  user ada logged in
ERROR disk almost full
INFO  user ada logged out
ERROR disk full
Prints
›ERROR disk almost full
›ERROR disk full

It prints ERROR disk almost full and ERROR disk full. Unlike readAllLines, Files.lines keeps the file open while you work through the stream and leaves closing it to you, so open it in try-with-resources, as here. Streams explains filter and forEach.

Read a file using Scanner#

A Scanner, the class many people first use for keyboard input, reads files too: give it a File in place of System.in. hasNextLine() asks whether another line is waiting, and nextLine() reads it:

Main.java
1import java.io.File;
2import java.io.IOException;
3import java.util.Scanner;
4
5public class Main {
6 public static void main(String[] args) throws IOException {
7 try (Scanner scanner = new Scanner(new File("notes.txt"))) {
8 while (scanner.hasNextLine()) {
9 String line = scanner.nextLine();
10 System.out.println(line.toUpperCase());
11 }
12 }
13 }
14}
Files before the program runs
notes.txt
Buy milk
Walk the dog
Call Maya
Prints
›BUY MILK
›WALK THE DOG
›CALL MAYA

It prints the three notes in capital letters. A Scanner on a file holds the file open like a reader, so it goes in try-with-resources too, and new Scanner(new File(...)) throws a FileNotFoundException when the file is missing. Where a Scanner saves the most work is a file of numbers, which the next section reads.

Turn each line into fields and numbers#

A line read from a file is always a String, even when it holds a number. To use its parts, cut it with split and convert the pieces. Each line of scores.csv holds a name and a score with a comma between them:

Main.java
1import java.io.IOException;
2import java.nio.file.Files;
3import java.nio.file.Path;
4import java.util.List;
5
6public class Main {
7 public static void main(String[] args) throws IOException {
8 List<String> lines = Files.readAllLines(Path.of("scores.csv"));
9 int total = 0;
10
11 for (String line : lines) {
12 String[] parts = line.split(",");
13 String name = parts[0];
14 int score = Integer.parseInt(parts[1]);
15 System.out.println(name + " scored " + score);
16 total += score;
17 }
18
19 System.out.println("Average: " + (double) total / lines.size());
20 }
21}
Files before the program runs
scores.csv
Ada,91
Grace,88
Alan,79
Prints
›Ada scored 91
›Grace scored 88
›Alan scored 79
›Average: 86.0

For the line Ada,91, split(",") gives the array ["Ada", "91"], and Integer.parseInt turns the text "91" into the number 91. Step through it and watch parts in the memory view on each pass. The scores add up to 258, and the cast to double keeps the fraction, so the last line is Average: 86.0.

Spaces and blank lines break the parsing
Integer.parseInt does not allow spaces, so a line written as Ada, 91 stops the program with java.lang.NumberFormatException: For input string: " 91". Call trim() on each piece first. A blank line, often left at the end of a file, splits into a single empty piece, so parts[1] fails with java.lang.ArrayIndexOutOfBoundsException: Index 1 out of bounds for length 1. Skip such lines with if (line.isBlank()) continue;.

A Scanner can do the converting for you. nextInt() reads the next whole number, skipping any spaces and line breaks in front of it, and hasNextInt() checks that one is coming:

Main.java
1import java.io.File;
2import java.io.IOException;
3import java.util.Scanner;
4
5public class Main {
6 public static void main(String[] args) throws IOException {
7 int total = 0;
8 int count = 0;
9
10 try (Scanner scanner = new Scanner(new File("numbers.txt"))) {
11 while (scanner.hasNextInt()) {
12 total += scanner.nextInt();
13 count++;
14 }
15 }
16
17 System.out.println(count + " numbers, total " + total);
18 }
19}
Files before the program runs
numbers.txt
12 7 30
5 18
Prints
›5 numbers, total 72

It prints 5 numbers, total 72. The numbers sit on two lines, and nextInt() does not mind, because a Scanner splits its input at any whitespace.

Where does Java look for the file?#

A name with no folder in it, like notes.txt, is a relative path. Java looks for it in the working directory, the folder the program was started from. Run java Main.javain a terminal and that is the folder you are in. In IntelliJ IDEA and VS Code it is the project's top folder by default, so the file goes there, next to src, not inside it.

When Java cannot find a file you can see, ask it where it is looking. toAbsolutePath() puts the working directory in front of the name. Run this one on your own computer, since its answer depends on where you start it:

Main.java
read along
1import java.nio.file.Path;
2
3public class Main {
4 public static void main(String[] args) {
5 Path file = Path.of("notes.txt");
6 System.out.println(file.toAbsolutePath());
7 }
8}

Started in the folder /Users/ada/hello on a Mac, it prints /Users/ada/hello/notes.txt. On Windows, started in C:\Users\ada\hello, it prints C:\Users\ada\hello\notes.txt. That is the exact place Java tried.

For a file inside a folder, give Path.ofeach part. It joins them with your system's separator, / on macOS and Linux and \ on Windows, so the same code works on both:

Main.java
1import java.io.IOException;
2import java.nio.file.Files;
3import java.nio.file.Path;
4
5public class Main {
6 public static void main(String[] args) throws IOException {
7 Path file = Path.of("data", "notes.txt");
8 System.out.println("Reading " + file);
9 System.out.print(Files.readString(file));
10 }
11}
Files before the program runs
data/notes.txt
Buy milk
Walk the dog
Call Maya
Prints
›Reading data/notes.txt
›Buy milk
›Walk the dog
›Call Maya

Here the first line is Reading data/notes.txt; on Windows it shows data\notes.txt. An absolute path, such as /Users/ada/notes.txt, starts from the top of the disk and works from any working directory, but only on a computer that has that exact folder.

Backslashes in Windows paths
In a Java string a backslash starts an escape, like \n for a line break. So "C:\Users\ada\notes.txt" does not compile (javac reports error: illegal escape character), and "C:\temp\new.txt" compiles but holds a tab and a line break where the folder separators should be, so it fails when the program runs. Double every backslash, "C:\\Users\\ada\\notes.txt", or use forward slashes, "C:/Users/ada/notes.txt", which Java accepts on Windows too.

What happens if the file does not exist?#

Reading a missing file throws an exception, and which one depends on how you read. The Files methods throw NoSuchFileException. FileReader and a Scanner over a File throw FileNotFoundException. Both are kinds of IOException, so a catch (IOException e) block handles either. This program asks for notez.txt, a typo, three ways, and prints each exception it catches:

Main.java
1import java.io.BufferedReader;
2import java.io.File;
3import java.io.FileReader;
4import java.io.IOException;
5import java.nio.file.Files;
6import java.nio.file.Path;
7import java.util.Scanner;
8
9public class Main {
10 public static void main(String[] args) {
11 try {
12 System.out.print(Files.readString(Path.of("notez.txt")));
13 } catch (IOException e) {
14 System.out.println(e);
15 }
16
17 try (BufferedReader reader = new BufferedReader(new FileReader("notez.txt"))) {
18 System.out.println(reader.readLine());
19 } catch (IOException e) {
20 System.out.println(e);
21 }
22
23 try (Scanner scanner = new Scanner(new File("notez.txt"))) {
24 System.out.println(scanner.nextLine());
25 } catch (IOException e) {
26 System.out.println(e);
27 }
28 }
29}
Files before the program runs
notes.txt
Buy milk
Walk the dog
Call Maya
Prints
›java.nio.file.NoSuchFileException: notez.txt
›java.io.FileNotFoundException: notez.txt (No such file or directory)
›java.io.FileNotFoundException: notez.txt (No such file or directory)

Printing an exception shows its full class name and its message. The part in brackets comes from the operating system, so on Windows it reads (The system cannot find the file specified). A real program would print a friendly sentence in the catch block, or ask for another name, and carry on. Without a catch, the exception stops the program. With throws IOException on main instead, the readString line would end it with Exception in thread "main" java.nio.file.NoSuchFileException: notez.txt and a stack trace.

Catch it or declare it
IOException is a checked exception: the compiler refuses any call that can throw it unless you catch it or declare throws IOException on the method. Paste Files.readString into a main that does neither, and javac stops with error: unreported exception IOException; must be caught or declared to be thrown. Exception handling explains both choices.

Files.exists(path)tells you whether the file is there before you read it, which suits a choice such as “create the settings file on the first run”. Keep the catch as well: the file can disappear between the check and the read.

Do you need to close the file?#

Only when you hold an open reader. Creating a FileReader, a Scanner over a file or the stream from Files.lines opens the file, and it stays open until you close it, using up one of the limited number of files the operating system lets a program have open. readString, readAllLines and the other one-call Files methods open and close the file inside the call, so they leave nothing to close.

Key idea

Open every reader, Scanner and Files.lines stream in the parentheses of a try, and Java closes it when the block ends, however the block ends.

A close() call at the bottom of the method works too, until an exception skips it. Reading files builds the reader, the loop and the closing up step by step.

Opening a file in another program
“Open a file” sometimes means showing it in the program that normally opens it, such as a PDF viewer, which is a different job from reading it. A desktop program does that with Desktop.getDesktop().open(new File("report.pdf")), from java.awt.

Character encoding: reading UTF-8#

A file stores bytes, and a character encoding is the rule that turns those bytes into letters. The Files methods read UTF-8 unless you pass another encoding. Since Java 18 (JEP 400), UTF-8 is also the default for FileReader and Scanneron every operating system. Before that they used the computer's own default, which on many Windows machines was not UTF-8, so older code names the encoding: new FileReader("notes.txt", StandardCharsets.UTF_8), a constructor added in Java 11.

A file saved in another encoding needs that encoding passed in, such as Files.readString(path, Charset.forName("windows-1252")). With the wrong encoding the ways fail differently: readString and readAllLines throw a MalformedInputException, FileReader puts the replacement character � in place of each character it cannot decode, and a Scanner stops as if the file had ended, without an error (its ioException() method returns the problem).

Which way should you use?#

WayWhat you getCloses the file itself?Use it when
Files.readString(path)The whole file as one StringYesYou want all the text at once, to print or search it
Files.readAllLines(path)A List<String>, one element per lineYesThe file fits in memory and you work line by line
Files.lines(path)A Stream<String> of lines, read as you goNo, use try-with-resourcesThe file is large, or you want to filter and count with streams
BufferedReader and readLine()One line per call, null at the endNo, use try-with-resourcesThe file is large, or your code must run on any Java version
Scanner over a FileLines, words or numbers, one at a timeNo, use try-with-resourcesThe file holds numbers you want as int or double values

For most files, start with Files.readAllLines: it is one line of code and leaves nothing to close. Switch to BufferedReader or Files.lines when a file is too big to load at once, and to Scanner when the file is mostly numbers. Path and Files covers the Files methods in depth. To save your results to disk, see how to write to a file in Java.

Questions people ask#

How do I read a file line by line in Java?
Wrap the file in a BufferedReader and call readLine() in a loop until it returns null, which marks the end of the file. If the file is small, Files.readAllLines(path) gives you every line in a List<String> in one call.
How do I read a whole file into a String in Java?
Call Files.readString(Path.of("notes.txt")), which arrived in Java 11. It returns all of the text, line breaks included, and closes the file before it returns.
How do I read a file using Scanner in Java?
Create new Scanner(new File("notes.txt")) in a try-with-resources block and loop with hasNextLine() and nextLine(). To read whole numbers instead, loop with hasNextInt() and nextInt().
Where do I put the file so Java can find it?
Put it in the working directory, the folder the program runs from, which in IntelliJ IDEA and VS Code is the project folder by default. Print Path.of("notes.txt").toAbsolutePath() to see exactly where Java looks.
Why do I get NoSuchFileException or FileNotFoundException?
Java did not find the file where it looked, because the name is misspelled or the file is in another folder. The Files methods throw NoSuchFileException, and FileReader and Scanner throw FileNotFoundException. Both are kinds of IOException, so one catch (IOException e) handles them.
How do I open a file in Java?
To read a file, open it with a reader such as new BufferedReader(new FileReader("notes.txt")) and close the reader when you finish, or let Files.readString and Files.readAllLines open and close it for you. To show a file in the program that normally opens it, such as a PDF viewer, a desktop program calls Desktop.getDesktop().open(file).

Further reading#

Learn it step by step

These lessons teach the topic from the start, with more examples and questions to check yourself.

All guides

Spotted a mistake, or a part that did not make sense? Report it by email