Delete the Contents of a File in Java Without Deleting It

Learn to delete the contents of a file in Java without deleting it, with Files.write, FileChannel.truncate and writers, plus partial truncation.

Before and after diagram comparing truncating a file to zero bytes, deleting it, and overwriting it without truncation, which leaves old bytes at the end

Deleting the contents of a file in Java without removing the file is called truncating, and we do it with Files.write(path, new byte[0]) or FileChannel.truncate(0), which leave the file with 0 bytes but keep its name, location and permissions. Any writer that opens the file without append mode, such as a FileWriter or a PrintWriter, truncates it the same way.

We clear files to reset a log or a scratch file between runs, to empty a cache file without changing its permissions, or to reuse an export file and keep only its header line. Truncating also releases the disk space that the old content used.

The following example fills a cache file and clears it twice, once with Files.write() and once with a FileChannel.

Path cache = Files.createTempFile("cache", ".txt");
Files.writeString(cache, "apple=5\nbanana=3\n");

Files.write(cache, new byte[0]);
long afterWrite = Files.size(cache);          // 0

Files.writeString(cache, "apple=5\n");
try (FileChannel channel = FileChannel.open(cache, StandardOpenOption.WRITE)) {
    channel.truncate(0);
}
long afterTruncate = Files.size(cache);       // 0
boolean stillThere = Files.exists(cache);     // true

Notice that the file still exists after both calls and only its size changed. We compare truncating with deleting, cover the NIO and java.io ways to empty a file, truncate to a given size, and look at the traps of writing without truncation and of clearing a file that another program has open.

1. Truncating a File vs Deleting It

Truncating sets the length of a file to a smaller value and discards the bytes after the new end. The file itself stays, with the same path, owner, permissions and creation time, and programs that have it open keep a valid handle. Deleting removes the file from the directory, so the next write has to create a new file with default permissions.

Before and after diagram comparing truncating a file to zero bytes, deleting it, and overwriting it without truncation, which leaves old bytes at the end
Truncating keeps the file and empties it, deleting removes it, and writing without TRUNCATE_EXISTING leaves the old bytes after the new ones

Say a service writes its PID file or a lock file with permissions rw——- that an admin set by hand. Deleting and recreating the file would reset those permissions, whereas truncating keeps them. Each operation fits a different need.

OperationCodeFile after the call
Truncate to zeroFiles.write(path, new byte[0])Exists, 0 bytes, same permissions
Truncate to n byteschannel.truncate(n)Exists, first n bytes kept
DeleteFiles.delete(path)Gone, the next write creates a new file
Overwrite without truncatingFiles.write(path, bytes, WRITE)Old bytes after the new data remain

When we want the file gone, the post on deleting files and directories covers Files.delete() and Files.deleteIfExists().

2. Deleting the Contents of a File With Files.write()

The method Files.write() without open options behaves as if we passed CREATE, TRUNCATE_EXISTING and WRITE. Writing an empty array therefore truncates the file and writes nothing, which leaves an empty file. Files.newOutputStream(path) does the same as soon as it opens the file.

The default options also create the file if it is missing, so a typo in the path creates a new empty file and leaves the real one untouched. When the file must already exist, we pass WRITE and TRUNCATE_EXISTING without CREATE.

Path dir = Files.createTempDirectory("app");
Path log = Files.writeString(dir.resolve("app.log"), "started\nstopped\n");

Files.write(log, new byte[0], StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING);
long size = Files.size(log);                                                               // 0

Path typo = Files.write(dir.resolve("ap.log"), new byte[0], StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING);   // NoSuchFileException

try (OutputStream out = Files.newOutputStream(log)) {
    out.write("restarted\n".getBytes(StandardCharsets.UTF_8));
}
List<String> content = Files.readAllLines(log);                                           // [restarted]

When we clear a file that must exist, we pass WRITE and TRUNCATE_EXISTING without CREATE, so a wrong path fails with a NoSuchFileException instead of creating an empty file. The last block shows the common case of clearing a file and writing new content in one step, which is what every write method does by default, as the guide on writing to a file in Java explains.

3. Truncating to a Given Size With FileChannel

The method FileChannel.truncate(long size) cuts the file at any byte position, not only at zero. If the given size is smaller than the file, the bytes after it are discarded, and if it is equal or larger, the file is not changed. The channel must be open for writing.

A shop exports its daily orders to the same orders.csv file, and a cleanup step empties the file but keeps the header row for the next run. The following example truncates the file right after the first line.

Path csv = Files.createTempFile("orders", ".csv");
Files.writeString(csv, "item,qty\napple,5\nbanana,3\n");
long headerBytes = "item,qty\n".getBytes(StandardCharsets.UTF_8).length;

try (FileChannel channel = FileChannel.open(csv, StandardOpenOption.WRITE)) {
    channel.truncate(headerBytes);
}
List<String> left = Files.readAllLines(csv);   // [item,qty]

FileChannel readOnly = FileChannel.open(csv, StandardOpenOption.READ);
FileChannel same = readOnly.truncate(0);       // NonWritableChannelException
readOnly.close();

The size is a byte count, so we compute it from the encoded bytes and not from String.length(), which counts characters. A channel opened only for reading throws a NonWritableChannelException. Positioned reads, writes and locks with FileChannel are explained in the post on NIO channels.

4. Using RandomAccessFile.setLength()

The older RandomAccessFile.setLength() does the same job as truncate() for code that works with java.io.File. Unlike truncate(), it can also make a file longer, in which case the new bytes have undefined content.

Path scratch = Files.createTempFile("scratch", ".dat");
Files.writeString(scratch, "temporary data");

try (RandomAccessFile file = new RandomAccessFile(scratch.toFile(), "rw")) {
    file.setLength(0);
}
long size = Files.size(scratch);   // 0

The mode “rw” opens the file for reading and writing and creates it if it is missing. A RandomAccessFile also exposes its channel through getChannel(), so both APIs can work on the same open file.

5. Opening a Writer Without Append Mode

Every writer and output stream that opens a file without append mode truncates it in the constructor. So we can clear a file by opening and closing a FileWriter, a PrintWriter, a FileOutputStream or a writer from Files.newBufferedWriter(). Writing an empty string or calling flush() adds nothing.

Path notes = Files.createTempFile("notes", ".txt");
File file = notes.toFile();

Files.writeString(notes, "old note");
new FileWriter(file, StandardCharsets.UTF_8).close();
long afterFileWriter = Files.size(notes);    // 0

Files.writeString(notes, "old note");
new PrintWriter(file, StandardCharsets.UTF_8).close();
long afterPrintWriter = Files.size(notes);   // 0

Files.writeString(notes, "old note");
new FileOutputStream(file).close();
long afterStream = Files.size(notes);        // 0

The one-liners work, but they hide the intent, because a reader sees a writer that writes nothing. When the goal is an empty file, Files.write(path, new byte[0]) or truncate(0) says what the code does. The flip side is a frequent bug, because a FileWriter opened without true as the append flag wipes a file that the code meant to extend.

6. Overwriting Without Truncating Leaves Old Bytes

The option WRITE on its own opens a file at position 0 but keeps its length. New data overwrites the start of the file, and when the new data is shorter than the old content, the rest of the old content stays after it.

Path greeting = Files.createTempFile("greeting", ".txt");
Files.writeString(greeting, "hello world");

Files.writeString(greeting, "Jim", StandardOpenOption.WRITE);
String broken = Files.readString(greeting);   // "Jimlo world"

Files.writeString(greeting, "Jim", StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING);
String fixed = Files.readString(greeting);    // "Jim"

The same happens with a FileChannel or a RandomAccessFile that writes at position 0. When we replace the whole content, we either add TRUNCATE_EXISTING, rely on the default options, or call truncate(position) after the last write.

7. Clearing a File That Another Program Is Writing

Clearing a log file while the app that writes it is still running is a common request. The result depends on how the other program opened the file. A writer without append mode keeps its own position, so after the truncation its next write lands at the old position, and the file starts with a gap of zero bytes. A writer in append mode always writes at the current end, so the file continues from the start.

Path log = Files.createTempFile("server", ".log");

try (OutputStream logger = new FileOutputStream(log.toFile())) {
    logger.write("12345".getBytes(StandardCharsets.UTF_8));
    try (FileChannel channel = FileChannel.open(log, StandardOpenOption.WRITE)) {
        channel.truncate(0);
    }
    logger.write("678".getBytes(StandardCharsets.UTF_8));
}
String withGap = Arrays.toString(Files.readAllBytes(log));   // [0, 0, 0, 0, 0, 54, 55, 56]

try (OutputStream logger = new FileOutputStream(log.toFile(), true)) {
    logger.write("12345".getBytes(StandardCharsets.UTF_8));
    try (FileChannel channel = FileChannel.open(log, StandardOpenOption.WRITE)) {
        channel.truncate(0);
    }
    logger.write("678".getBytes(StandardCharsets.UTF_8));
}
String clean = Files.readString(log);                        // "678"

On Windows, another program can also hold the file open with a lock, and the truncation fails with a FileSystemException. For logs, the cleaner option is the rotation feature of the logging framework, which closes the file, renames it and opens a new one.

8. Clear File Contents FAQs

Emptying a whole file takes one line, whereas clearing only a few lines or getting the disk space back takes more thought.

8.1. How Do I Delete Only Some Lines of a File?

There is no way to remove bytes from the middle of a file. We read the lines, keep the ones we want and write the result back, ideally to a temp file that we move over the original.

Path todo = Files.createTempFile("todo", ".txt");
Files.write(todo, List.of("milk", "eggs", "bread"));

List<String> kept = Files.readAllLines(todo).stream().filter(line -> !line.equals("eggs")).toList();
Path tmp = Files.createTempFile(todo.getParent(), "todo", ".tmp");
Files.write(tmp, kept);
Files.move(tmp, todo, StandardCopyOption.REPLACE_EXISTING);
List<String> result = Files.readAllLines(todo);   // [milk, bread]

Removing lines from the end is the one exception, because truncate() with the byte position of the last line we keep does it in place.

8.2. Does Truncating a File Free Disk Space?

Yes. The file system releases the blocks after the new end of the file. If another program still holds data for the file in its own buffer, it can write that data back later, which is one more reason to stop or rotate the writer first.

8.3. Does Files.write(path, new byte[0]) Work on Java 8?

Yes. Files.write() with a byte[] exists since Java 7, and so do FileChannel.truncate() and StandardOpenOption.TRUNCATE_EXISTING. Only Files.writeString() needs Java 11, so Files.writeString(path, “”) is the Java 11 version of the same call.

8.4. Is There a Commons IO Method to Clear a File?

Commons IO has no method named for it, but FileUtils.write(file, “”, StandardCharsets.UTF_8) truncates the file and creates missing parent directories. Without the library, Files.write(path, new byte[0]) does the same in one line, as shown in section 2.

9. Conclusion

To delete the contents of a file in Java, we truncate it. Files.write(path, new byte[0]) empties a file in one line, FileChannel.truncate(n) cuts it at any byte position, and every writer opened without append mode truncates the file as it opens it.

We pass WRITE and TRUNCATE_EXISTING when a wrong path must fail instead of creating a file, and we never overwrite with WRITE alone when the new content is shorter. For files that another program writes, append mode or log rotation avoids gaps of zero bytes. Other file tasks, from copying to watching a directory, are grouped in the Java IO guide.

10. References

Happy Learning !!

Source Code on Github

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.