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.
import java.io.IOException;import java.nio.file.Files;import java.nio.file.Path;import java.util.List; public class Main { public static void main(String[] args) throws IOException { Path file = Path.of("notes.txt"); String text = Files.readString(file); System.out.print(text); List<String> lines = Files.readAllLines(file); System.out.println(lines); System.out.println("The file has " + lines.size() + " lines."); }}Buy milk Walk the dog Call Maya
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.
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:
import java.io.BufferedReader;import java.io.FileReader;import java.io.IOException; public class Main { public static void main(String[] args) throws IOException { try (BufferedReader reader = new BufferedReader(new FileReader("notes.txt"))) { String line; int number = 1; while ((line = reader.readLine()) != null) { System.out.println(number + ": " + line); number++; } } }}Buy milk Walk the dog 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:
import java.io.BufferedReader;import java.io.FileReader;import java.io.IOException;import java.nio.file.Files;import java.nio.file.Path; public class Main { public static void main(String[] args) throws IOException { Files.writeString(Path.of("notes.txt"), "Buy milk\nWalk the dog\nCall Maya\n"); try (BufferedReader reader = new BufferedReader(new FileReader("notes.txt"))) { while (reader.readLine() != null) { System.out.println(reader.readLine()); } } }}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:
import java.io.IOException;import java.nio.file.Files;import java.nio.file.Path;import java.util.stream.Stream; public class Main { public static void main(String[] args) throws IOException { try (Stream<String> lines = Files.lines(Path.of("server.log"))) { lines.filter(line -> line.startsWith("ERROR")) .forEach(System.out::println); } }}INFO server started INFO user ada logged in ERROR disk almost full INFO user ada logged out 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:
import java.io.File;import java.io.IOException;import java.util.Scanner; public class Main { public static void main(String[] args) throws IOException { try (Scanner scanner = new Scanner(new File("notes.txt"))) { while (scanner.hasNextLine()) { String line = scanner.nextLine(); System.out.println(line.toUpperCase()); } } }}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:
import java.io.IOException;import java.nio.file.Files;import java.nio.file.Path;import java.util.List; public class Main { public static void main(String[] args) throws IOException { List<String> lines = Files.readAllLines(Path.of("scores.csv")); int total = 0; for (String line : lines) { String[] parts = line.split(","); String name = parts[0]; int score = Integer.parseInt(parts[1]); System.out.println(name + " scored " + score); total += score; } System.out.println("Average: " + (double) total / lines.size()); }}Ada,91 Grace,88 Alan,79
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.
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:
import java.io.File;import java.io.IOException;import java.util.Scanner; public class Main { public static void main(String[] args) throws IOException { int total = 0; int count = 0; try (Scanner scanner = new Scanner(new File("numbers.txt"))) { while (scanner.hasNextInt()) { total += scanner.nextInt(); count++; } } System.out.println(count + " numbers, total " + total); }}12 7 30 5 18
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:
import java.nio.file.Path; public class Main { public static void main(String[] args) { Path file = Path.of("notes.txt"); System.out.println(file.toAbsolutePath()); }}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:
import java.io.IOException;import java.nio.file.Files;import java.nio.file.Path; public class Main { public static void main(String[] args) throws IOException { Path file = Path.of("data", "notes.txt"); System.out.println("Reading " + file); System.out.print(Files.readString(file)); }}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.
\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:
import java.io.BufferedReader;import java.io.File;import java.io.FileReader;import java.io.IOException;import java.nio.file.Files;import java.nio.file.Path;import java.util.Scanner; public class Main { public static void main(String[] args) { try { System.out.print(Files.readString(Path.of("notez.txt"))); } catch (IOException e) { System.out.println(e); } try (BufferedReader reader = new BufferedReader(new FileReader("notez.txt"))) { System.out.println(reader.readLine()); } catch (IOException e) { System.out.println(e); } try (Scanner scanner = new Scanner(new File("notez.txt"))) { System.out.println(scanner.nextLine()); } catch (IOException e) { System.out.println(e); } }}Buy milk Walk the dog Call Maya
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.
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 ideaOpen every reader,
ScannerandFiles.linesstream in the parentheses of atry, 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.
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?#
| Way | What you get | Closes the file itself? | Use it when |
|---|---|---|---|
Files.readString(path) | The whole file as one String | Yes | You want all the text at once, to print or search it |
Files.readAllLines(path) | A List<String>, one element per line | Yes | The file fits in memory and you work line by line |
Files.lines(path) | A Stream<String> of lines, read as you go | No, use try-with-resources | The file is large, or you want to filter and count with streams |
BufferedReader and readLine() | One line per call, null at the end | No, use try-with-resources | The file is large, or your code must run on any Java version |
Scanner over a File | Lines, words or numbers, one at a time | No, use try-with-resources | The 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
BufferedReaderand callreadLine()in a loop until it returnsnull, which marks the end of the file. If the file is small,Files.readAllLines(path)gives you every line in aList<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 withhasNextLine()andnextLine(). To read whole numbers instead, loop withhasNextInt()andnextInt(). - 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
Filesmethods throwNoSuchFileException, andFileReaderandScannerthrowFileNotFoundException. Both are kinds ofIOException, so onecatch (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 letFiles.readStringandFiles.readAllLinesopen and close it for you. To show a file in the program that normally opens it, such as a PDF viewer, a desktop program callsDesktop.getDesktop().open(file).