Skip to content
Preview · sample content · Explore all pages

Java file I/O: UTF-8, streaming reads, and path ownership

Last updated: 28 Sept 20263 min read
tutorial
IntermediateBy AITrove Editorial

Java NIO file APIs read and write data through Path values while letting the caller choose encoding, buffering, and resource lifetime.

A path is not an open file

Constructing a Path identifies a filesystem location. Opening a reader performs I/O and can fail for missing files, permissions, resource limits, or concurrent changes. Treat those failures as part of the boundary’s contract.

The program creates its own temporary file, writes a small UTF-8 batch, reads it with a BufferedReader, and removes the file in finally. It does not touch an existing user-selected file. The contents use a known numeric format so the parsing rule is explicit.

An explicit charset prevents platform defaults from changing interpretation across machines. A writer and reader must agree on the format as well as the encoding. Lines with commas, quoted fields, or embedded newlines need a parser for that format rather than an improvised split.

Keep the input size bounded

readAllLines materialises the entire file. A buffered line loop can process one line at a time, but it still needs a maximum line length or bounded input policy for hostile files. Streaming does not turn unlimited input into a safe workload.

Here each accepted line holds an invoice number, and the loop counts records. A real importer should specify whether one invalid line aborts the batch or produces a per-line rejection report.

Do not let an untrusted filename directly select arbitrary storage paths. Directory containment, symlink handling, and the time between validation and opening all matter. This lesson uses a path created by the process to keep those issues outside the demonstration.

Working program

Java
import java.io.BufferedReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;

public class Utf8InvoiceBatch {
    public static void main(String[] args) throws Exception {
        Path batch = Files.createTempFile("aitrove-invoices-", ".txt");
        try {
            Files.write(batch, Arrays.asList("1041", "1042", "1043"), StandardCharsets.UTF_8);
            int records = 0;
            try (BufferedReader reader = Files.newBufferedReader(batch, StandardCharsets.UTF_8)) {
                String line;
                while ((line = reader.readLine()) != null) {
                    Integer.parseInt(line);
                    records++;
                }
            }
            System.out.println("records=" + records);
        } finally {
            Files.deleteIfExists(batch);
        }
    }
}

Output

Output
records=3

Cost and design choices

The read visits B input characters, giving O(B) read and parse work for this bounded numeric format. It retains one current line and reader buffering rather than all records; working space depends on the longest line and buffer size.

This example writes an in-memory list first, so its fixture preparation is not a streaming writer for an enormous batch. The read pattern remains useful when the file already exists.

File deletion in a finally block can itself fail and mask a previous error. A production cleanup boundary should preserve the primary failure and report a failed deletion separately. Resource closing provides suppression semantics for closeable resources.

Common Mistakes

  • Do not rely on the default charset for a defined text format.
  • Do not load an unbounded file into memory without a size policy.
  • Do not swallow I/O errors and report an empty successful batch.
java
files
Storage details