Java BufferedReader: How to Read a File with Example

⚡ Smart Summary

Java BufferedReader reads text from an input stream by buffering characters, which makes line-by-line file reading fast and simple. Wrapping a FileReader inside BufferedReader and looping on readLine until it returns null prints an entire file.

  • 📖 Core Purpose: BufferedReader wraps any Reader whose read operations are costly, reducing individual reads against the underlying stream.
  • 🔁 Read Loop: readLine returns one line per call and null at end of file, so a while loop on that result walks the whole file.
  • 🧱 Escaped Paths: A Windows path inside a Java string needs a doubled backslash, otherwise the compiler rejects the invalid escape sequence.
  • ♻️ Resource Release: Closing the reader frees the underlying file handle; try-with-resources performs that close automatically.
  • ⌨️ Console Input: Wrapping System.in in an InputStreamReader lets the same class read keyboard input rather than a file.
  • ⚖️ Scanner Contrast: BufferedReader is faster and returns raw lines, while Scanner parses tokens and primitive types directly.
  • 🔤 Charset Caution: FileReader applies the default charset, so specifying one explicitly avoids garbled characters across machines.

BufferedReader in Java

How to read a file in Java?

Java provides several mechanisms to read from a file. The most useful package for this is java.io, whose abstract Reader class defines character-stream reading. java.io.BufferedReader is the concrete subclass used for efficient line-based reading.

What is BufferedReader in Java?

BufferedReader is a Java class that reads text from an input stream, such as a file, by buffering characters so that characters, arrays, and lines are read efficiently. Without buffering, each read request made of a Reader triggers a corresponding read request against the underlying character or byte stream.

It is therefore advisable to wrap BufferedReader around any Reader whose read() operations may be costly, such as FileReader and InputStreamReader. A typical usage passes the file path to the reader as follows:

// Note the doubled backslash: a single \ starts an escape sequence
objReader = new BufferedReader(new FileReader("D:\\DukesDiary.txt"));

This loads the file into objReader. You then iterate through the contents of the file and print each line. The while loop in the code below reads the file until it reaches the end of the file.

while ((strCurrentLine = objReader.readLine()) != null) {
    System.out.println(strCurrentLine);
}

strCurrentLine holds the current line, and objReader.readLine() returns a String. The loop therefore keeps iterating for as long as the returned value is not null, and stops on the first null, which signals end of file.

BufferedReader Example

The code below is a complete Java BufferedReader example:

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class ReadFileExample {

    public static void main(String[] args) {
        BufferedReader objReader = null;
        try {
            String strCurrentLine;

            objReader = new BufferedReader(new FileReader("D:\\DukesDiary.txt"));

            while ((strCurrentLine = objReader.readLine()) != null) {
                System.out.println(strCurrentLine);
            }

        } catch (IOException e) {
            e.printStackTrace();

        } finally {
            try {
                if (objReader != null)
                    objReader.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
}

Note: The finally block matters. It guarantees that objReader.close() runs whether or not an exception was thrown, which releases the underlying file handle and keeps memory management predictable. The nested try inside finally is required because close() itself declares IOException.

BufferedReader JDK7 Example

From JDK 7 onward, try-with-resources removes the finally block entirely. Any resource declared in the parentheses of the try statement is closed automatically when the block exits, in reverse order of creation, even if an exception propagates:

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class ReadFileExample_jdk7 {

    private static final String FILENAME = "D:\\DukesDiary.txt";

    public static void main(String[] args) {

        try (BufferedReader br = new BufferedReader(new FileReader(FILENAME))) {

            String strCurrentLine;

            while ((strCurrentLine = br.readLine()) != null) {
                System.out.println(strCurrentLine);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

This version is shorter and safer, so it is the form to prefer in new code. Reading a file is only one use of the class; the next section compares it with the alternative most beginners reach for first.

BufferedReader vs Scanner: Which to Use

Both classes read text, and beginners often pick one arbitrarily. They solve different problems, and the wrong choice shows up as either clumsy manual parsing or poor throughput once the input grows to thousands of lines. The table below sets them side by side.

Aspect BufferedReader Scanner
Returns Whole lines as String Parsed tokens and primitives
Parsing built in No — you split or convert yourself Yes — nextInt, nextDouble, nextLine
Default buffer 8192 characters 1024 characters
Speed on large files Faster Slower, because of regex tokenizing
Thread safety Synchronized Not synchronized
Best for Reading large files line by line Small interactive input needing typed values

Choose BufferedReader when the goal is to move through a file quickly and handle the text yourself, and Scanner when you want typed values without writing conversion code. Splitting each returned line with the String split method gives BufferedReader most of Scanner’s convenience at higher speed.

How to Read Console Input Using BufferedReader

The same class reads keyboard input, which is useful for small command-line programs and for coding-challenge input. System.in is a byte stream, so it cannot be handed to BufferedReader directly. An InputStreamReader bridges bytes to characters, and BufferedReader then adds efficient line reading on top of that bridge.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class ReadConsoleExample {

    public static void main(String[] args) throws IOException {

        // Bridge the System.in byte stream to a character stream
        try (BufferedReader reader =
                new BufferedReader(new InputStreamReader(System.in))) {

            System.out.print("Enter your name: ");
            String name = reader.readLine();

            System.out.print("Enter your age: ");
            // readLine always returns text, so convert it explicitly
            int age = Integer.parseInt(reader.readLine().trim());

            System.out.println("Hello " + name + ", age " + age);
        }
    }
}

Three details are worth remembering. First, readLine() always returns a String, so numeric input must be converted with Integer.parseInt or similar, and a non-numeric entry throws NumberFormatException — wrap the conversion if the input is untrusted. Second, calling .trim() removes the trailing carriage return that Windows consoles append, a frequent cause of parse failures. Third, avoid closing a reader wrapped around System.in if the program still needs console input later, because closing the wrapper closes the underlying stream permanently for that process.

For programs that read many values, reading one line and splitting it is far faster than calling readLine() repeatedly, since each call crosses into the operating system only when the buffer empties. This is the main reason competitive programmers prefer BufferedReader over Scanner for bulk input.

Even with correct code, a handful of errors appear repeatedly.

Common BufferedReader Errors and How to Fix Them

Most BufferedReader failures come from file paths, resource handling, or character encoding rather than from the reading loop itself. Each error below names its exception and its fix.

  • Illegal escape character: the path uses a single backslash, as in "D:\DukesDiary.txt". Double it to "D:\\DukesDiary.txt", or use a forward slash, which Java accepts on Windows.
  • FileNotFoundException: the path is relative to the working directory, not the source file. Print new File(name).getAbsolutePath() to see where Java is actually looking.
  • NullPointerException on the first read: the reader was never assigned because the constructor threw, and the exception was swallowed. Check the exception handling before the loop.
  • Garbled or question-mark characters: the file is UTF-8 but FileReader applied the platform default charset. Pass a charset explicitly, for example new InputStreamReader(new FileInputStream(f), StandardCharsets.UTF_8).
  • Stream closed: the reader was closed inside the loop or reused after try-with-resources ended. Open a fresh reader for each pass over the file.

FAQs

Yes. A second constructor argument sets the buffer in characters, for example new BufferedReader(reader, 16384). The 8192 default suits most files; raising it helps only with very large sequential reads.

For small files, yes — it returns a List of lines in one call. For large files prefer Files.newBufferedReader or Files.lines, because readAllLines holds the entire file in memory at once.

Yes. AI assistants rewrite finally-block cleanup into try-with-resources and suggest NIO equivalents. Verify the charset and exception behaviour afterwards, because generated conversions often drop the explicit encoding.

AI reads the stack trace and identifies whether the cause is a wrong working directory, a missing permission, or an encoding mismatch. It shortens diagnosis, though the fix still needs verification against the real file.

No. readLine strips the line terminator, so a returned String contains no trailing newline. An empty line in the file therefore returns an empty String rather than null.

Summarize this post with: