Files.readString()

Read a whole small file into one String in a single call, choose the right charset, and know when the file is too big for it.

Signature
public static String readString(Path path, Charset cs) throws IOException
  • Added in Java Java 11

Parameters

ParameterTypeDescription
$pathPathThe file to read.
$csoptionalCharset= StandardCharsets.UTF_8The charset used to decode the bytes. Leave it out to use readString(Path), which decodes as UTF-8.

Return value

StringThe entire contents of the file decoded with the given charset (UTF-8 when the one-argument overload is used).

Errors

IOException if the file cannot be read (NoSuchFileException when it does not exist, AccessDeniedException without permission); MalformedInputException, a subclass of IOException, when the bytes are not valid in the charset; OutOfMemoryError when the file is extremely large, for example over 2 GB.

Files.readString(path) opens the file, reads every byte, decodes it and closes the file again. It arrived in Java 11 and is the shortest correct way to load a small text file: a JSON config, an SQL script, an email template, a test fixture.

Charset

The one-argument form always decodes as UTF-8, whatever the platform default is. Pass a Charset when the file comes from somewhere that does not write UTF-8, such as an old export from a Windows system. If the bytes are not valid for the charset, the method does not quietly insert replacement characters: it throws MalformedInputException. That is usually what you want in a service, because a half-decoded config is worse than a clear failure.

Size is the real limit

The whole file lands in the heap, and a String holds the decoded characters, so memory use is at least the file size. That is fine for a few kilobytes. For an uploaded CSV, a log, or anything a user controls, either check Files.size(path) first and refuse large files, or stream it with Files.lines or Files.newBufferedReader. A file over 2 GB cannot fit in one String at all and fails with OutOfMemoryError.

Mistakes people make

  • Calling Files.exists first and then reading. The file can disappear in between; catch NoSuchFileException instead.
  • Reading a file from inside the application JAR with Path.of. Classpath resources are not files once packaged; use getResourceAsStream and readAllBytes.
  • Relying on relative paths. They resolve against the working directory, which differs between your IDE, a container and a systemd unit.
String json = Files.readString(Path.of("config/app.json"));
String legacy = Files.readString(Path.of("import/old.txt"), StandardCharsets.ISO_8859_1);

In a web service, read configuration once at startup and keep the result, rather than reading the file on every request.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Load a JSON or YAML configuration file once at service startup and pass the text to a parser.
  • Read an HTML or email template from disk and fill in placeholders before sending a notification.
  • Load expected-response fixtures in integration tests and compare them with what an endpoint returned.

Frequently asked questions

How do I read a whole file into a String in Java?

Since Java 11, call Files.readString(Path.of("file.txt")). It reads the entire file as UTF-8 and closes it for you. On Java 8, the equivalent is new String(Files.readAllBytes(path), StandardCharsets.UTF_8), which silently replaces invalid bytes with U+FFFD instead of throwing.

Why does Files.readString throw MalformedInputException?

The file contains bytes that are not valid in the charset, usually a file saved as ISO-8859-1 or Windows-1252 being read as UTF-8. Pass the correct charset as the second argument, or convert the file to UTF-8.

Is Files.readString safe for large files?

No. It loads everything into memory and fails for files over about 2 GB. Use Files.lines or Files.newBufferedReader to process large files line by line.

More examples

Reading a file that is not UTF-8

The byte 0xE9 is not valid UTF-8 on its own, so the default overload throws instead of guessing. Decoding with the charset the file was really written in gives the four expected characters, the last one being U+00E9.

Example · java
void main() throws IOException {
    Path file = Files.createTempFile("legacy", ".txt");
    try {
        // "cafe" with an accented e, as an old ISO-8859-1 system writes it: one byte 0xE9.
        Files.write(file, new byte[] {'c', 'a', 'f', (byte) 0xE9});

        try {
            Files.readString(file);
        } catch (MalformedInputException e) {
            IO.println("UTF-8 failed: " + e.getMessage());
        }

        String text = Files.readString(file, StandardCharsets.ISO_8859_1);
        IO.println("Latin-1 length: " + text.length());
        IO.println("last char code: " + (int) text.charAt(3));
    } finally {
        Files.deleteIfExists(file);
    }
}

// Output:
// UTF-8 failed: Input length = 1
// Latin-1 length: 4
// last char code: 233

Optional file with a fallback

Catching NoSuchFileException handles the missing case without a separate exists check that could race with another process. Any other IOException, such as a permissions problem, still propagates, which is what you want for a real fault.

Example · java
String loadOrDefault(Path file, String fallback) throws IOException {
    try {
        return Files.readString(file);
    } catch (NoSuchFileException e) {
        return fallback;
    }
}

void main() throws IOException {
    Path dir = Files.createTempDirectory("cfg");
    Path banner = dir.resolve("banner.txt");
    try {
        IO.println(loadOrDefault(banner, "Welcome"));
        Files.writeString(banner, "Scheduled maintenance at 02:00 UTC");
        IO.println(loadOrDefault(banner, "Welcome"));
    } finally {
        Files.deleteIfExists(banner);
        Files.deleteIfExists(dir);
    }
}

// Output:
// Welcome
// Scheduled maintenance at 02:00 UTC

Refusing files that are too large

A size check before readString protects the heap when the file comes from a user or another system. Anything over the limit should be streamed with Files.lines or a BufferedReader instead.

Example · java
static final long MAX_BYTES = 64 * 1024;

String readSmall(Path file) throws IOException {
    long size = Files.size(file);
    if (size > MAX_BYTES) {
        throw new IOException(file.getFileName() + " is " + size + " bytes, limit is " + MAX_BYTES);
    }
    return Files.readString(file);
}

void main() throws IOException {
    Path dir = Files.createTempDirectory("tpl");
    Path small = dir.resolve("welcome.html");
    Path big = dir.resolve("export.csv");
    try {
        Files.writeString(small, "<h1>Hello {{name}}</h1>");
        Files.writeString(big, "id,total\n".repeat(10_000));

        IO.println(readSmall(small).replace("{{name}}", "Ada"));
        try {
            readSmall(big);
        } catch (IOException e) {
            IO.println("rejected: " + e.getMessage());
        }
    } finally {
        Files.deleteIfExists(small);
        Files.deleteIfExists(big);
        Files.deleteIfExists(dir);
    }
}

// Output:
// <h1>Hello Ada</h1>
// rejected: export.csv is 90000 bytes, limit is 65536

Discussion

  • Be the first to comment on this lesson.