Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Java bounded line reader: reject oversized records without buffering the file

Last updated: 5 Oct 20264 min read
tutorial
IntermediateBy AITrove Editorial

A line-oriented parser should cap each record before accumulating an unbounded String or list of lines.

Apply the limit while reading

The reader accepts a fixed maximum of content characters per line. A newline ends the record; a preceding carriage return is removed for CRLF input. Count characters as they arrive and reject the first one beyond the limit. Reading every line first and checking lengths afterward loses the memory bound.

The implementation returns null only when EOF arrives before any record character. A final line without a newline is still valid. See file I/O ownership for closing an actual file reader and strict decoding if malformed bytes must be rejected before characters reach this parser.

Define what a character limit means

This limit counts UTF-16 code units because Reader.read returns char values. A protocol limit expressed in UTF-8 bytes or Unicode code points needs a different counter. Keep the unit explicit in the API contract, especially when supplementary characters are permitted.

Working program

Java
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;

public class BoundedManifestLine {
    static String readLine(Reader source, int maximumChars) throws IOException {
        if (maximumChars < 0) throw new IllegalArgumentException("negative limit");
        StringBuilder line = new StringBuilder();
        int next;
        while ((next = source.read()) != -1) {
            if (next == '\n') break;
            if (line.length() >= maximumChars + 1L) throw new IOException("line too long");
            line.append((char) next);
        }
        if (next == -1 && line.length() == 0) return null;
        if (line.length() > 0 && line.charAt(line.length() - 1) == '\r') line.setLength(line.length() - 1);
        if (line.length() > maximumChars) throw new IOException("line too long");
        return line.toString();
    }

    public static void main(String[] args) throws IOException {
        System.out.println(readLine(new StringReader("lot-47\r\n"), 6));
        try { readLine(new StringReader("shipment-82"), 6); }
        catch (IOException rejected) { System.out.println(rejected.getMessage()); }
    }
}

Output

Output
lot-47
line too long

Cost and ownership

The parser is O(n) in characters read and O(limit) in retained line storage. It allows one extra code unit temporarily so a terminal CR can be distinguished from content, then enforces the final content cap. A Reader still needs a byte-level input cap if decoding expansion or source volume matters.

Common Mistakes

  • Do not call readAllLines on an untrusted large file before validation.
  • Do not confuse UTF-16 code units with UTF-8 bytes.
  • Do not discard a valid last line merely because it lacks a newline.

Read next

Java file I/O: UTF-8, streaming reads, and path ownership, Java UTF-8 decoding: reject malformed bytes before parsing, Java InputStream short reads: assemble a complete record header, Java Stream.close: terminal traversal does not close every source.

java
io
bounded-line-reader
Storage details