Question

How to Read a File into a String in Java

javastringfilefile-ioio

Question

How can I read the full contents of a file into a String in Java?

I have been using code like this:

private String readFile(String file) throws IOException {
    BufferedReader reader = new BufferedReader(new FileReader(file));
    String line;
    StringBuilder stringBuilder = new StringBuilder();
    String ls = System.getProperty("line.separator");

    try {
        while ((line = reader.readLine()) != null) {
            stringBuilder.append(line);
            stringBuilder.append(ls);
        }

        return stringBuilder.toString();
    } finally {
        reader.close();
    }
}

This approach seems common, but is there a better or more modern way to read a file into a string in Java? I would also like to understand any trade-offs, such as handling line breaks, character encoding, and resource management.

Short Answer

By the end of this page, you will understand how to read a whole file into a String in Java, why character encoding matters, how modern Java APIs simplify the task, and when older approaches like BufferedReader are still useful.

Concept

Reading a file into a String means loading all text from a file into memory as one Java object.

This is a common task when you need to:

  • load configuration files
  • read templates
  • process JSON, XML, CSV, or plain text
  • run tests using sample input files

The core idea is simple, but there are three important details:

1. Character encoding

A file stores bytes, not characters. Java must decode those bytes into text using an encoding such as UTF-8.

If you use the wrong encoding, text may become corrupted. For example:

  • é may display incorrectly
  • non-English characters may break
  • symbols may be lost or replaced

That is why modern Java code often specifies StandardCharsets.UTF_8 explicitly.

2. Resource management

Files must be closed after reading. If not, your program can leak file handles.

Older code often uses try/finally to close readers manually. Modern Java uses try-with-resources, which closes resources automatically.

3. Preserving content exactly

Methods like readLine() remove line terminators. If you rebuild the string manually, you may accidentally:

  • change line endings
  • add an extra newline at the end
  • lose the file's original formatting

Modern methods such as Files.readString() or Files.readAllBytes() usually make this easier and safer.

In real programming, choosing the right method depends on:

  • your Java version
  • file size
  • whether you need exact file contents
  • whether you need to process line by line instead of loading everything at once

Mental Model

Think of a text file like a sealed box full of letters.

  • The file is the box.
  • The bytes are the raw paper slips inside.
  • The character encoding is the language guide that tells Java how to interpret those slips.
  • The String is the final readable message.

If you use the wrong guide, the message looks wrong. If you open the box and forget to close it, you leave a mess behind. If you copy line by line and add your own separators, you may not reproduce the original message exactly.

So the safest approach is often: open the file correctly, decode it using the right encoding, and let Java return the full text directly.

Take Quiz

Syntax and Examples

Modern Java: Files.readString()

If you are using Java 11 or later, this is the clearest approach:

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;

public class FileExample {
    public static String readFile(String filePath) throws IOException {
        return Files.readString(Path.of(filePath), StandardCharsets.UTF_8);
    }
}

Why this is good

  • short and readable
  • automatically handles opening and closing internally
  • lets you specify encoding explicitly
  • preserves the file content as text

Java 7+: Files.readAllBytes()

If Files.readString() is not available, you can do this:

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;

public class FileExample {
    public static String readFile(String filePath) throws IOException {
        byte[] bytes = Files.readAllBytes(Path.of(filePath));
        return new String(bytes, StandardCharsets.UTF_8);
    }
}

This reads the whole file into a byte array, then converts it into a String.

Using BufferedReader with try-with-resources

If you want to keep a reader-based approach, write it like this:

import java.io.BufferedReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;

public class FileExample {
    public static String readFile(String filePath) throws IOException {
        StringBuilder sb = new StringBuilder();

        try (BufferedReader reader = Files.newBufferedReader(Path.of(filePath), StandardCharsets.UTF_8)) {
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line).append(System.lineSeparator());
            }
        }

        return sb.toString();
    }
}

This is better than FileReader because:

  • it uses try-with-resources
  • it can specify encoding
  • it uses System.lineSeparator() instead of a system property lookup

Important note about line breaks

The readLine() method removes line endings. That means the result may not exactly match the original file unless you rebuild them carefully.

If exact content matters, prefer:

String content = Files.readString(Path.of("data.txt"), StandardCharsets.UTF_8);

Step by Step Execution

Consider this code:

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;

public class Demo {
    public static void main(String[] args) throws IOException {
        String text = Files.readString(Path.of("example.txt"), StandardCharsets.UTF_8);
        System.out.println(text);
    }
}

Assume example.txt contains:

Hello
Java

What happens step by step

1. Path.of("example.txt")

Java creates a Path object representing the file location.

2. Files.readString(...)

Java opens the file and reads all bytes from it.

3. StandardCharsets.UTF_8

Java decodes those bytes into characters using UTF-8.

4. A String is created

The full file contents become one String, including line breaks.

So text becomes something like:

"Hello\nJava\n"

The exact newline characters depend on the file content.

5. System.out.println(text)

The string is printed to the console.

Output:

Hello
Java

Line-by-line version trace

Now compare with this approach:

try (BufferedReader reader = Files.newBufferedReader(Path.of("example.txt"), StandardCharsets.UTF_8)) {
    String line;
    StringBuilder sb = new StringBuilder();

    while ((line = reader.readLine()) != null) {
        sb.append(line).append(System.lineSeparator());
    }

    System.out.println(sb.toString());
}

Step by step:

  1. Open the file.
  2. Read the first line: "Hello".
  3. Append "Hello".
  4. Append your system line separator.
  5. Read the second line: "Java".
  6. Append "Java".
  7. Append your system line separator.
  8. End of file reached.
  9. Convert StringBuilder to String.

This works, but it rebuilds line endings instead of preserving the original ones exactly.

Real World Use Cases

Reading a file into a string is useful in many real applications:

Loading JSON or XML

A backend service may read a JSON file before parsing it.

String json = Files.readString(Path.of("config.json"), StandardCharsets.UTF_8);

Reading SQL scripts

Applications often load .sql files and run them against a database.

Email or HTML templates

A server might read a template file into a string, then replace placeholders.

Test fixtures

Automated tests often compare expected output with the contents of a saved text file.

Import tools

A script may read log files, CSV data, or plain-text reports into memory for processing.

Configuration loading

Small config files are commonly loaded fully into memory because they are easy to parse once stored in a string.

This approach is best when the file is reasonably small and you really need the entire content at once.

Take Quiz

Real Codebase Usage

In real projects, developers usually choose one of these patterns:

1. Small file, whole content needed

Use Files.readString().

String template = Files.readString(path, StandardCharsets.UTF_8);

Common for:

  • templates
  • config files
  • test resources
  • small data files

2. Need explicit validation

Developers often check whether the file exists before reading.

if (!Files.exists(path)) {
    throw new IOException("File not found: " + path);
}
String content = Files.readString(path, StandardCharsets.UTF_8);

This is a guard clause: fail early if input is invalid.

3. Stream or process lines

If the file may be large, developers avoid loading the whole thing into memory.

try (var lines = Files.lines(path, StandardCharsets.UTF_8)) {
    lines.filter(line -> !line.isBlank())
         .forEach(System.out::println);
}

This is common in data processing and log analysis.

4. Wrap low-level file reading in utility methods

Teams often create helper methods such as:

public static String readUtf8File(Path path) throws IOException {
    return Files.readString(path, StandardCharsets.UTF_8);
}

This keeps encoding choices consistent across the codebase.

5. Error handling with useful messages

Real code often adds context when rethrowing exceptions.

try {
    return Files.readString(path, StandardCharsets.UTF_8);
} catch (IOException e) {
    throw new IOException("Failed to read file: " + path, e);
}

That makes debugging easier.

Common Mistakes

1. Using FileReader without thinking about encoding

FileReader uses the platform default encoding unless you use a newer constructor overload. That can make your program behave differently on different machines.

Risky code

BufferedReader reader = new BufferedReader(new FileReader("data.txt"));

Better

BufferedReader reader = Files.newBufferedReader(Path.of("data.txt"), StandardCharsets.UTF_8);

2. Forgetting to close the reader

Broken code

BufferedReader reader = Files.newBufferedReader(Path.of("data.txt"), StandardCharsets.UTF_8);
String line = reader.readLine();

If you never close the reader, resources can leak.

Better

try (BufferedReader reader = Files.newBufferedReader(Path.of("data.txt"), StandardCharsets.UTF_8)) {
    String line = reader.readLine();
}

3. Accidentally changing line endings

readLine() removes line separators.

Broken assumption

String line = reader.readLine();

Some beginners assume the returned string still includes \n, but it does not.

Better choice when exact content matters

String content = Files.readString(Path.of("data.txt"), StandardCharsets.UTF_8);

4. Adding an extra newline at the end

This code adds a line separator after every line, including the last one:

while ((line = reader.readLine()) != null) {
    sb.append(line).append(System.lineSeparator());
}

That may create a different result from the original file.

5. Loading huge files into memory

Reading a full file into a string is not a good idea for very large files.

Better for large files

try (BufferedReader reader = Files.newBufferedReader(Path.of("large.txt"), StandardCharsets.UTF_8)) {
    String line;
    while ((line = reader.readLine()) != null) {
        // process one line at a time
    }
}

6. Using System.getProperty("line.separator")

This works, but System.lineSeparator() is clearer.

Better

String ls = System.lineSeparator();

Comparisons

ApproachJava VersionEncoding ControlPreserves Original Text ExactlyBest For
Files.readString(path, charset)11+YesUsually yesSmall text files, simplest modern option
Files.readAllBytes(path) + new String(...)7+YesYesWhole-file reading when readString is unavailable
BufferedReader.readLine() + StringBuilderAny modern JavaYes, if created correctlyNo, not automaticallyLine-by-line handling or custom processing
FileReader + BufferedReaderOlder styleOften weak or implicitNo, not automaticallyLegacy code only

Files.readString() vs BufferedReader

  • Use Files.readString() when you want the whole file as text.
  • Use BufferedReader when you want to process lines individually.

readAllBytes() vs readLine()

  • readAllBytes() preserves the raw file bytes before decoding.
  • readLine() is useful for line-based logic, but it removes line endings.

FileReader vs Files.newBufferedReader()

  • Files.newBufferedReader() fits better with Path and explicit charsets.
  • FileReader is older and easier to misuse with default encoding.
Take Quiz

Cheat Sheet

Read whole file into a string

Java 11+

String text = Files.readString(Path.of("file.txt"), StandardCharsets.UTF_8);

Java 7+

String text = new String(Files.readAllBytes(Path.of("file.txt")), StandardCharsets.UTF_8);

Read line by line

try (BufferedReader reader = Files.newBufferedReader(Path.of("file.txt"), StandardCharsets.UTF_8)) {
    String line;
    while ((line = reader.readLine()) != null) {
        // use line
    }
}

Key rules

  • Prefer Path and Files over older FileReader code.
  • Specify encoding explicitly, usually StandardCharsets.UTF_8.
  • Use try-with-resources when working with readers or streams.
  • readLine() removes line separators.
  • Reading the whole file is fine for small files, not ideal for huge ones.

Common choices

  • Whole file, modern Java: Files.readString()
  • Whole file, older Java: Files.readAllBytes()
  • Large file or per-line logic: BufferedReader or Files.lines()

Edge cases

  • Wrong encoding can corrupt text.
  • Rebuilding lines may add or change newline characters.
  • Missing file throws IOException.

FAQ

What is the easiest way to read a file into a string in Java?

If you use Java 11 or later, Files.readString(path, StandardCharsets.UTF_8) is usually the easiest and clearest option.

Why should I specify UTF-8 explicitly?

If you do not specify an encoding, your program may depend on the machine's default encoding, which can cause inconsistent results.

Does BufferedReader.readLine() keep newline characters?

No. It returns each line without the line terminator.

Is it okay to read an entire file into memory?

Yes, for small or moderate text files. For very large files, it is better to process the file incrementally.

Should I still use FileReader?

Usually, modern code prefers Files.newBufferedReader() or Files.readString() because they work well with Path and explicit charsets.

What happens if the file does not exist?

Java throws an IOException, commonly a NoSuchFileException when using NIO APIs.

How do I preserve the exact file content?

Use Files.readString() or Files.readAllBytes() instead of rebuilding text with readLine().

What if I need to handle each line separately?

Use BufferedReader or Files.lines() so you can process one line at a time.

Take Quiz

Mini Project

Description

Build a small Java utility that loads a text template from a file and prints it to the console. This demonstrates the most common real-world use of reading a file into a string: loading full text content such as templates, config snippets, or sample data.

Goal

Create a program that reads a UTF-8 text file into a single String and displays its contents safely.

Requirements

  • Read the file path from a String variable in the program.
  • Load the full file contents as UTF-8 text.
  • Print the contents to the console.
  • Show a friendly error message if the file cannot be read.
Take Quiz

Keep learning

Related questions

View more