Java
Programming
File Handling
Text Files
Code Tutorial

Reading a plain text file in Java

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Reading a plain text file is one of the most common Java I/O tasks, but there is more than one good API for it. The right choice depends on whether you want the whole file at once, one line at a time, explicit character encoding, or a streaming approach for larger files.

Start With Files for Modern Java

For most modern Java code, the java.nio.file.Files API is the cleanest starting point. It works with Path objects and makes common operations concise.

If you want the entire file as one string:

java
1import java.io.IOException;
2import java.nio.charset.StandardCharsets;
3import java.nio.file.Files;
4import java.nio.file.Path;
5
6public class Main {
7    public static void main(String[] args) throws IOException {
8        Path path = Path.of("example.txt");
9        String content = Files.readString(path, StandardCharsets.UTF_8);
10
11        System.out.println(content);
12    }
13}

This is ideal for small files such as templates, configuration snippets, or test fixtures.

If you want the file as individual lines:

java
1import java.io.IOException;
2import java.nio.charset.StandardCharsets;
3import java.nio.file.Files;
4import java.nio.file.Path;
5import java.util.List;
6
7public class Main {
8    public static void main(String[] args) throws IOException {
9        Path path = Path.of("example.txt");
10        List<String> lines = Files.readAllLines(path, StandardCharsets.UTF_8);
11
12        for (String line : lines) {
13            System.out.println(line);
14        }
15    }
16}

That works well when the file is modest in size and line structure matters.

Use BufferedReader for Streaming Reads

If the file may be large, do not load everything into memory just because it is convenient. BufferedReader lets you process the file incrementally.

java
1import java.io.BufferedReader;
2import java.io.IOException;
3import java.nio.charset.StandardCharsets;
4import java.nio.file.Files;
5import java.nio.file.Path;
6
7public class Main {
8    public static void main(String[] args) throws IOException {
9        Path path = Path.of("example.txt");
10
11        try (BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
12            String line;
13            while ((line = reader.readLine()) != null) {
14                System.out.println(line);
15            }
16        }
17    }
18}

This pattern is still one of the most practical choices in production code because it is explicit, efficient, and easy to control.

You can also use the stream-based API:

java
1import java.io.IOException;
2import java.nio.charset.StandardCharsets;
3import java.nio.file.Files;
4import java.nio.file.Path;
5
6public class Main {
7    public static void main(String[] args) throws IOException {
8        Path path = Path.of("example.txt");
9
10        try (var lines = Files.lines(path, StandardCharsets.UTF_8)) {
11            lines.filter(line -> !line.isBlank())
12                 .forEach(System.out::println);
13        }
14    }
15}

This is especially nice when you want to combine reading with filtering or mapping.

Character Encoding Matters

A plain text file is only “plain” if both sides agree on the encoding. Many bugs blamed on file reading are really encoding mismatches.

That is why examples above specify StandardCharsets.UTF_8 instead of relying on the platform default. Explicit encoding makes the behavior stable across machines, containers, and CI environments.

If you omit the charset and the file was written in a different encoding, you may see:

  • garbled non-ASCII text
  • unexpected replacement characters
  • parsing failures in downstream logic

When the input format is under your control, standardize on UTF-8 and read it explicitly.

Which API Should You Choose

A practical rule of thumb is:

  • use Files.readString for small whole-file reads
  • use Files.readAllLines when you need all lines in memory
  • use BufferedReader or Files.lines for larger or streamed processing

There is no prize for using the most low-level class. Choose the API that matches the size and shape of the data.

For example, a configuration file with twenty lines is a fine candidate for readString. A gigabyte log file is not.

Error Handling and Resource Safety

File access can fail for ordinary reasons:

  • the file does not exist
  • permissions are missing
  • the path points to a directory
  • the file is locked or unreadable

That is why these examples either declare throws IOException or use a try block. Also notice the try-with-resources pattern when a reader or stream is opened. It guarantees the resource is closed cleanly.

Ignoring cleanup is a classic source of file-handle leaks in long-running applications.

Common Pitfalls

The most common pitfall is using the platform default charset and assuming it will behave the same everywhere. Be explicit about encoding.

Another mistake is reading a large file fully into memory when a streamed approach would be simpler and safer.

A third issue is forgetting to close readers and streams. Use try-with-resources unless a higher-level framework manages the lifecycle for you.

Finally, developers sometimes use Scanner for everything. It can be fine for token-oriented parsing, but it is not automatically the best text-file reader for every workload.

Summary

  • In modern Java, Files and Path are the normal starting point for reading text files.
  • Use readString or readAllLines for small files and BufferedReader or Files.lines for larger ones.
  • Specify UTF-8 explicitly when encoding matters, which is most of the time.
  • Use try-with-resources to avoid leaking file handles.
  • Match the API to the file size and processing style instead of forcing one approach everywhere.

Course illustration
Course illustration