Files.writeString()

Write text to a file in one call, control create, overwrite and append with open options, and avoid half-written files.

Signature
public static Path writeString(Path path, CharSequence csq, Charset cs, OpenOption... options) throws IOException
  • Added in Java Java 11

Parameters

ParameterTypeDescription
$pathPathThe file to write.
$csqCharSequenceThe text to write, such as a String or StringBuilder. Written exactly as given; no line separator is added.
$csoptionalCharset= StandardCharsets.UTF_8The charset used to encode the text. Leave it out to use writeString(Path, CharSequence, OpenOption...), which encodes as UTF-8.
$optionsoptionalOpenOption...= CREATE, TRUNCATE_EXISTING, WRITEHow the file is opened, for example StandardOpenOption.APPEND or CREATE_NEW. With no options the method behaves as if CREATE, TRUNCATE_EXISTING and WRITE were given.

Return value

PathThe path that was written, so calls can be chained.

Errors

IOException if the file cannot be created or written (NoSuchFileException when the parent directory is missing, FileAlreadyExistsException with CREATE_NEW), or when the text cannot be encoded in the charset (UnmappableCharacterException); IllegalArgumentException for an invalid combination of options, such as APPEND with TRUNCATE_EXISTING or READ; UnsupportedOperationException for an unsupported option.

Files.writeString(path, text) encodes the text as UTF-8, writes it and closes the file. With no options it creates the file if needed and truncates it if it already exists, so the call replaces the old contents completely.

Open options change the behaviour

Files.writeString(p, text);                                      // create or replace
Files.writeString(p, text, StandardOpenOption.APPEND);           // add to the end; file must exist
Files.writeString(p, text, StandardOpenOption.CREATE,
                           StandardOpenOption.APPEND);           // create if missing, then append
Files.writeString(p, text, StandardOpenOption.CREATE_NEW);       // fail if the file exists

Once you pass any option, the defaults no longer apply. APPEND on its own throws NoSuchFileException for a missing file, which surprises people who expected it to create one. CREATE_NEW is the race-free way to claim a file name: the check and the creation happen as one operation, so two requests cannot both win.

No line separators are added

The method writes exactly the characters you pass. If each call should produce a line, add "\n" yourself. Files.write(path, lines) is the line-oriented sibling and adds a separator after every line.

Half-written files

Truncate-then-write means a reader can see an empty or partial file while a write is in progress, and a crash can leave it that way. For files other processes read (a cache, a generated config, a feed), write to a temporary file in the same directory and then Files.move it over the target with ATOMIC_MOVE. Readers then see either the old file or the new one. Files.createTempFile makes the file owner-only (rw-------) on Linux, and the move keeps those permissions, so set them before the move if a process running as another user must read the result.

The parent directory must exist; call Files.createDirectories(path.getParent()) first when it might not. For large or streaming output, use Files.newBufferedWriter rather than building one huge String in memory.

Example

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

When to use it

  • Write a generated nginx or service config file that another process reloads, using a temporary file and an atomic move.
  • Save a nightly CSV export of orders to a reports directory for a finance team to collect.
  • Claim a lock or marker file with CREATE_NEW so only one copy of a scheduled job runs at a time on a host.

Frequently asked questions

How do I append to a file instead of overwriting it in Java?

Pass open options: Files.writeString(path, text, StandardOpenOption.CREATE, StandardOpenOption.APPEND). Without CREATE, APPEND throws NoSuchFileException when the file does not exist.

Does Files.writeString add a newline?

No. It writes exactly the characters you pass. Add "\n" yourself, or use Files.write(path, listOfLines), which adds the platform line separator after every line.

Why does Files.writeString throw NoSuchFileException?

Either the parent directory does not exist, or you passed APPEND without CREATE for a file that is not there yet. Call Files.createDirectories(path.getParent()) first, or add StandardOpenOption.CREATE.

More examples

Replacing a file atomically

A reader polling feature-flags.json never sees an empty or partial file, because the rename swaps the whole file in one step. The finally block removes the temporary file if anything failed before the move. The temporary file is created owner-only on Linux, so the published file is too unless you change its permissions first.

Example · java
void writeAtomically(Path target, String content) throws IOException {
    // The temporary file must be in the same directory (same file system) for an atomic move.
    Path tmp = Files.createTempFile(target.getParent(), "write-", ".tmp");
    try {
        Files.writeString(tmp, content);
        Files.move(tmp, target, StandardCopyOption.ATOMIC_MOVE);
    } finally {
        Files.deleteIfExists(tmp);
    }
}

void main() throws IOException {
    Path dir = Files.createTempDirectory("flags");
    Path flags = dir.resolve("feature-flags.json");
    try {
        writeAtomically(flags, "{\"newCheckout\": false}");
        writeAtomically(flags, "{\"newCheckout\": true}");
        IO.println(Files.readString(flags));
        try (Stream<Path> files = Files.list(dir)) {
            IO.println("files in dir: " + files.count());
        }
    } finally {
        Files.deleteIfExists(flags);
        Files.deleteIfExists(dir);
    }
}

// Output:
// {"newCheckout": true}
// files in dir: 1

Writing a CSV export

writeString accepts any CharSequence, so a StringBuilder can be written without calling toString. The explicit '\n' gives the same line endings on every operating system, which matters when another service parses the file.

Example · java
record Order(int id, String customer, long totalPence) {}

void main() throws IOException {
    List<Order> orders = List.of(
        new Order(1001, "Ada", 2599),
        new Order(1002, "Linus", 1200));

    StringBuilder csv = new StringBuilder("id,customer,total_pence\n");
    for (Order o : orders) {
        csv.append(o.id()).append(',').append(o.customer()).append(',').append(o.totalPence()).append('\n');
    }

    Path file = Files.createTempFile("orders", ".csv");
    try {
        Files.writeString(file, csv);
        List<String> lines = Files.readAllLines(file);
        IO.println(lines.size() + " lines, " + Files.size(file) + " bytes");
        IO.println(lines.get(1));
    } finally {
        Files.deleteIfExists(file);
    }
}

// Output:
// 3 lines, 54 bytes
// 1001,Ada,2599

Text the charset cannot encode

writeString refuses to replace characters it cannot encode, and in the JDK the text is encoded before the file is opened, so nothing is written. With the default UTF-8 the two accented letters take two bytes each, which is why the byte count and the character count differ.

Example · java
void main() throws IOException {
    Path dir = Files.createTempDirectory("enc");
    Path file = dir.resolve("name.txt");
    String name = "Zo\u00EB Fran\u00E7ois";
    try {
        try {
            Files.writeString(file, name, StandardCharsets.US_ASCII);
        } catch (UnmappableCharacterException e) {
            IO.println("cannot encode: " + e.getMessage());
        }
        IO.println("file created: " + Files.exists(file));

        Files.writeString(file, name);
        IO.println("UTF-8 bytes: " + Files.size(file) + " for " + name.length() + " chars");
    } finally {
        Files.deleteIfExists(file);
        Files.deleteIfExists(dir);
    }
}

// Output:
// cannot encode: Input length = 1
// file created: false
// UTF-8 bytes: 14 for 12 chars

Discussion

  • Be the first to comment on this lesson.