The file.truncate([size]) method resizes an open file stream to a specified byte length, discarding trailing bytes to reclaim disk storage or extending the file with zero bytes.
Required Access Mode: Truncating requires write permissions. Opening in read-only mode ('r') raises an io.UnsupportedOperation exception. For in-place resizing without erasing existing content on open, always use mode='r+' or mode='r+b'.
The standard CPython signature for file.truncate() is:
file_object.truncate(size=None)
| Parameter | Type | Default | Behavior & Description |
|---|---|---|---|
size |
int | None |
None |
Target size of the file in bytes. If omitted or None, the file is truncated to the current file pointer position as reported by tell(). |
| Return Value | int |
— | Returns the new file size in bytes. |
| Exceptions | io.UnsupportedOperation |
— | Raised when attempting to truncate a stream opened in read-only mode (e.g. 'r' or 'rb'). |
Choosing the correct mode when invoking truncate() is critical to prevent unintended data destruction:
| File Mode | Truncate Supported? | Open Behavior | Recommended Role for truncate() |
|---|---|---|---|
'r+' |
Yes | Preserves file; positions at 0 | Gold Standard: Inspect, seek, and resize in-place without erasing contents. |
'r' |
No (raises error) | Read-only | Cannot truncate. Raises io.UnsupportedOperation: File not open for writing. |
'a' / 'a+' |
Yes | Preserves file; pointer at EOF | Useful for truncating to an explicit byte limit (e.g. f.truncate(100)). |
'w' / 'w+' |
Yes (Redundant) | Erases file immediately to 0 bytes | File is already empty upon opening, rendering truncate() largely redundant. |
Python offers two distinct execution mechanics for resizing files:
# Pattern 1: Explicit byte limit
with open("archive.log", "r+", encoding="utf-8") as f:
# Truncate to exactly 100 bytes regardless of cursor position
new_size = f.truncate(100)
print("File resized to:", new_size)
# Pattern 2: Implicit truncation at current cursor position (f.tell())
with open("records.txt", "r+", encoding="utf-8") as f:
f.readline() # Consume header row
f.readline() # Consume row 1
# Truncate everything after the first two lines
f.truncate()
print("File truncated cleanly at current cursor offset:", f.tell())
When you pass a target size parameter, Python supports both down-sizing and up-sizing:
size < current_size): Bytes past the new boundary are removed, and filesystem blocks are marked for deallocation.size > current_size): The file grows to the larger size. The newly allocated sparse space between the old end-of-file and the new boundary reads as null bytes (\x00 or 0x00).with open("sparse.bin", "r+b") as bin_stream:
# Extend file to 1024 bytes; padding filled with \x00 bytes
bin_stream.truncate(1024)
print("File expanded to 1 KB with zero-byte padding.")
A crucial technical detail is that truncate() does not alter the current file pointer offset. If your stream cursor was at byte 50 and you call f.truncate(20), the file pointer remains at byte 50 — now beyond the new EOF!
with open("data.txt", "r+", encoding="utf-8") as f:
f.seek(50)
f.truncate(20)
# Notice: tell() is still at 50, past the new end of file
print("Cursor offset:", f.tell()) # 50
# Always seek back to a valid range before performing further I/O
f.seek(0)
Reset an existing file to zero bytes without deleting the file or losing open OS file descriptors:
def clear_file_contents(filepath: str) -> None:
with open(filepath, "r+", encoding="utf-8") as stream:
stream.seek(0)
stream.truncate(0)
clear_file_contents("temp_buffer.txt")
print("File cleared to 0 bytes safely in-place.")
Cap oversized logs to a maximum byte threshold to prevent local disk exhaustion:
import os
def cap_log_file_size(log_path: str, max_bytes: int = 1048576) -> None:
if os.path.exists(log_path) and os.path.getsize(log_path) > max_bytes:
with open(log_path, "r+b") as f:
f.truncate(max_bytes)
cap_log_file_size("debug.log", 50000)
print("Oversized log capped within quota bounds.")
Attempting to call truncate() in read mode ('r') raises io.UnsupportedOperation: File not open for writing. Always use 'r+' or 'r+b'.
In append mode ('a'), the stream pointer sits at EOF by default. Calling bare f.truncate() without a size argument truncates at EOF — effectively doing nothing. Pass an explicit integer size (e.g. f.truncate(100)) if truncating in append mode.
Run this complete test harness verifying file shrinking, pointer truncation, and zero-byte clearing:
import tempfile
import os
class TruncateVerifier:
@staticmethod
def shrink(path: str, size: int) -> int:
with open(path, "r+b") as stream:
stream.truncate(size)
return os.path.getsize(path)
# Verification unit tests
with tempfile.TemporaryDirectory() as td:
v = TruncateVerifier()
test_file = os.path.join(td, "verify.bin")
initial_bytes = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" # 36 bytes
with open(test_file, "wb") as f:
f.write(initial_bytes)
# Test 1: Shrink to exactly 10 bytes
assert v.shrink(test_file, 10) == 10
with open(test_file, "rb") as f:
assert f.read() == b"0123456789"
# Test 2: Truncate at current seek position (implicit size)
with open(test_file, "r+b") as f:
f.seek(4)
f.truncate()
assert os.path.getsize(test_file) == 4
# Test 3: Clear file completely
with open(test_file, "r+b") as f:
f.truncate(0)
assert os.path.getsize(test_file) == 0
print("All file.truncate() unit assertions passed successfully!")
Open in Google Colab View on GitHub
Run all file truncate exercises interactively, test 'r+' in-place resizing, experiment with sparse file extension padding, and clear files in Google Colab.
Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.