Question

How to Read a Large Text File Line by Line in Java

javaperformancefile-ioiotext-files

Question

I need to read a very large text file, around 5 to 6 GB in size, line by line using Java.

What is the correct and efficient way to do this?

For example, I want to process one line at a time without loading the entire file into memory.

Short Answer

By the end of this page, you will understand how to read large text files efficiently in Java without running out of memory. You will learn the most common approaches, when to use BufferedReader or Files.newBufferedReader, how line-by-line reading works, and what mistakes to avoid when processing multi-gigabyte files.

Concept

When a file is very large, you should not try to load the whole file into memory at once. A 5–6 GB text file is much larger than the heap size of many Java programs, so reading it all at once can cause memory problems.

The key idea is streaming the file: read a small portion, get one line, process it, then move on to the next line. This keeps memory usage low because only a small buffer and the current line are held in memory.

In Java, the most common way to do this is with:

  • BufferedReader
  • Files.newBufferedReader(...)
  • Files.lines(...) in some cases

BufferedReader is important because it reads data using an internal buffer. Without buffering, your program may perform many slow disk reads. With buffering, Java reads larger chunks internally and gives you lines efficiently.

Why this matters in real programming:

  • Log processing often involves gigabytes of text.
  • Import jobs read CSV or TSV files one line at a time.
  • Batch systems process records from large exports.
  • Data cleanup scripts need to scan files without exhausting memory.

The main goal is to balance memory efficiency, readability, and performance. For most beginner and production use cases, BufferedReader is the simplest and safest solution.

Mental Model

Think of a huge text file like a very long roll of paper.

You do not unroll the whole thing across the floor just to read it.

Instead, you:

  1. Unroll a small section
  2. Read the next line
  3. Process it
  4. Move forward

BufferedReader works like a smart reading window over that roll of paper. It keeps a small chunk ready so reading is faster, but it never tries to hold the entire roll in memory.

Take Quiz

Syntax and Examples

The most common pattern is to use BufferedReader with a loop.

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

public class LargeFileReader {
    public static void main(String[] args) {
        Path path = Path.of("data.txt");

        try (BufferedReader reader = Files.newBufferedReader(path)) {
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

How this works

  • Files.newBufferedReader(path) opens the file for reading.
  • reader.readLine() reads one line at a time.
  • When there are no more lines, it returns null.
  • The try-with-resources block automatically closes the file.

Specifying a character encoding

If the file encoding is known, it is better to specify it explicitly.

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 LargeFileReader {
    public static void main(String[] args) {
        Path path = Path.of("data.txt");

        try (BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
            String line;
            while ((line = reader.readLine()) != null) {
                // Process the line
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Alternative using BufferedReader directly

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

public class LargeFileReader {
    public static void main(String[] args) {
        try (BufferedReader reader = new BufferedReader(new FileReader("data.txt"))) {
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

This also works, but Files.newBufferedReader(...) is often preferred in modern Java because it integrates well with Path and lets you set the charset clearly.

Step by Step Execution

Consider this example:

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

public class Demo {
    public static void main(String[] args) throws IOException {
        Path path = Path.of("sample.txt");

        try (BufferedReader reader = Files.newBufferedReader(path)) {
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println("Read: " + line);
            }
        }
    }
}

Assume sample.txt contains:

apple
banana
orange

Execution trace

  1. Path.of("sample.txt") creates a Path object pointing to the file.
  2. Files.newBufferedReader(path) opens the file and creates a buffered reader.
  3. String line; declares a variable to hold each line.
  4. The loop runs reader.readLine() for the first time.
    • It returns "apple".
    • line != null, so the loop body runs.
    • Output: Read: apple
  5. The loop runs again.
    • reader.readLine() returns "banana".
    • Output: Read: banana
  6. The loop runs again.
    • reader.readLine() returns "orange".
    • Output: Read: orange
  7. The loop runs again.
    • reader.readLine() returns null because the file is finished.
    • The loop stops.
  8. The try-with-resources block closes the reader automatically.

Important detail

At no point does Java store the whole file in memory. It only keeps:

  • the internal buffer
  • the current line
  • any data your processing code stores intentionally

Real World Use Cases

Reading large files line by line is common in many real applications.

Log file analysis

A server may generate multi-GB log files. You can scan them line by line to:

  • count errors
  • find failed requests
  • extract IP addresses
  • build summaries

CSV import jobs

A business system may receive exported data files with millions of rows. Reading line by line lets you:

  • validate each row
  • transform values
  • write results to a database
  • skip bad records without crashing the whole import

ETL and batch processing

Data pipelines often process text records from legacy systems. A line-by-line reader helps with:

  • record transformation
  • filtering
  • enrichment
  • output generation

Search and reporting tools

You might scan a large text file looking for:

  • matching keywords
  • duplicate records
  • formatting errors
  • summary statistics

Command-line utilities

Many Java scripts and internal tools need to process files too large to fit into memory. A streaming approach makes those tools practical and reliable.

Take Quiz

Real Codebase Usage

In real projects, developers usually do more than just print each line. They combine line-by-line reading with common patterns.

Validation

Each line is checked before processing.

if (line.isBlank()) {
    continue;
}

This skips empty lines early.

Guard clauses

Developers often return or continue early when a line is invalid.

if (!line.contains(",")) {
    continue;
}

This keeps the main logic simpler.

Counting and metrics

It is common to track how much work has been done.

long lineCount = 0;
while ((line = reader.readLine()) != null) {
    lineCount++;
}

Error handling per line

A real import job may log bad lines but continue processing.

try {
    String[] parts = line.split(",");
    // process parts
} catch (Exception ex) {
    System.err.println("Failed to process line: " + line);
}

Writing transformed output

Many systems read one file and write another as they go, instead of storing all results first.

Avoiding unnecessary object creation

In performance-sensitive code, developers try to avoid heavy work inside the loop unless needed. For example, they may avoid compiling the same regex repeatedly for each line.

Charset awareness

Production code often specifies UTF-8 explicitly instead of relying on platform defaults.

Common Mistakes

Here are common beginner mistakes when reading large files.

1. Reading the whole file into memory

Broken approach:

import java.nio.file.Files;
import java.nio.file.Path;

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

Why it is a problem:

  • This tries to load the entire file into memory.
  • It is unsafe for very large files.

Use line-by-line reading instead.

2. Using readLine() incorrectly

Broken code:

String line = reader.readLine();
while (line != null) {
    System.out.println(line);
}

Problem:

  • line is never updated inside the loop.
  • This creates an infinite loop.

Correct version:

String line;
while ((line = reader.readLine()) != null) {
    System.out.println(line);
}

3. Forgetting to close the reader

Broken code:

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

Problem:

  • The file handle may remain open.
  • This can cause resource leaks.

Use try-with-resources.

4. Ignoring character encoding

Broken code may work on one machine and fail on another if the platform default charset differs.

Better:

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

5. Storing every line anyway

Even if you read line by line, memory problems return if you do this:

List<String> allLines = new ArrayList<>();
String line;
while ((line = reader.readLine()) != null) {
    allLines.add(line);
}

If the file is huge, this defeats the purpose of streaming.

6. Doing very expensive work inside the loop without thinking

For huge files, even small inefficiencies matter. Repeated heavy operations per line can make processing much slower.

Comparisons

ApproachMemory UsageGood for Large Files?Notes
Files.readAllLines()HighNoLoads all lines into memory
Files.readString()HighNoLoads entire file as one string
BufferedReader.readLine()LowYesStandard and practical choice
Files.newBufferedReader()LowYesModern way to create a BufferedReader
Files.lines()LowYes, with careStream-based style; must close stream

BufferedReader vs Files.lines()

FeatureBufferedReaderFiles.lines()
StyleImperative loopStream API
Beginner-friendlyVeryModerate
Explicit controlHighMedium
Good for simple line processingYesYes
Risk of keeping stream open accidentallyLower with try-with-resourcesStill needs proper closing

Example of Files.lines()

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Stream;

public class LargeFileReader {
    public static void main(String[] args) {
        try (Stream<String> lines = Files.lines(Path.of("data.txt"))) {
            lines.forEach(System.out::println);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

This can be elegant, but for beginners, BufferedReader is often easier to understand and debug.

Take Quiz

Cheat Sheet

// Recommended pattern
try (BufferedReader reader = Files.newBufferedReader(Path.of("data.txt"))) {
    String line;
    while ((line = reader.readLine()) != null) {
        // process line
    }
}

Quick rules

  • Use BufferedReader for large text files.
  • Read one line at a time with readLine().
  • Stop when readLine() returns null.
  • Use try-with-resources to close the file automatically.
  • Specify charset like StandardCharsets.UTF_8 when possible.
  • Do not use Files.readAllLines() or Files.readString() for multi-GB files.
  • Do not collect all lines into a list unless the file is small.

Useful imports

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

Safe UTF-8 pattern

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

Edge cases

  • Very long individual lines may still require significant memory.
  • Empty lines are returned as "", not null.
  • End of file is signaled by null.
  • Processing may be slow if your per-line logic is expensive, even if reading is efficient.

FAQ

Should I use BufferedReader for a 5 GB text file in Java?

Yes. It is one of the standard and most efficient ways to read a large text file line by line without loading the full file into memory.

Is Files.readAllLines() safe for very large files?

No. It loads the entire file into memory, which is not suitable for multi-gigabyte files.

What is the fastest way to read a large text file line by line in Java?

For most cases, BufferedReader created with Files.newBufferedReader() is a strong default. It is efficient, simple, and memory-friendly.

Should I specify the file encoding?

Yes, if you know it. Using StandardCharsets.UTF_8 avoids problems caused by different platform default encodings.

Can I use Java Streams to read lines from a file?

Yes. Files.lines(path) returns a stream of lines. It is useful, but you still need to close it properly.

Why is my memory usage still high when reading line by line?

You may be storing processed lines in a collection, building large strings, or doing work inside the loop that keeps references alive.

What happens if the file has empty lines?

readLine() returns an empty string for an empty line. It returns null only when the file is finished.

Take Quiz

Mini Project

Description

Build a small Java program that scans a large log file and counts how many lines contain the word ERROR. This demonstrates practical line-by-line file processing without loading the whole file into memory.

Goal

Create a program that reads a text file one line at a time and prints the total number of matching lines.

Requirements

  • Read the file using a memory-efficient line-by-line approach.
  • Count each line that contains the text ERROR.
  • Skip empty lines.
  • Print the final count after reading the whole file.
Take Quiz

Keep learning

Related questions

View more