Python os.ftruncate Function
Last modified April 11, 2025
This comprehensive guide explores Python's os.ftruncate function,
which modifies file size by truncating or extending it. We'll cover file
descriptors, size modification, and practical file manipulation examples.
Basic Definitions
The os.ftruncate function changes the size of a file referenced
by a file descriptor. It can either truncate the file or extend it with null
bytes if the new size is larger than current size.
Key parameters: fd (file descriptor), length (new size in bytes). Requires write permission on the file. Works on both Unix and Windows systems.
Basic File Truncation
This example demonstrates the simplest use of os.ftruncate to
reduce a file's size. We first create a sample file with some content.
import os
# Create a sample file
with open("sample.txt", "w") as f:
f.write("This is a sample text file with some content.")
# Open file for reading and writing
with open("sample.txt", "r+") as f:
# Get current size
original_size = os.fstat(f.fileno()).st_size
print(f"Original size: {original_size} bytes")
# Truncate to 10 bytes
os.ftruncate(f.fileno(), 10)
truncated_size = os.fstat(f.fileno()).st_size
print(f"Truncated size: {truncated_size} bytes")
# Read remaining content
f.seek(0)
print(f"Content after truncation: '{f.read()}'")
This code creates a file, then opens it in read-write mode. It shows the original size, truncates to 10 bytes, and displays the new size and content.
The file descriptor is obtained from the file object using fileno(). After truncation, the file contains only the first 10 bytes of the original content.
Extending a File
os.ftruncate can also extend files by adding null bytes when the
new size exceeds the current size. This example demonstrates this behavior.
import os
# Create a small file
with open("small.txt", "w") as f:
f.write("short")
# Open and extend the file
with open("small.txt", "r+") as f:
print(f"Original content: '{f.read()}'")
f.seek(0)
# Extend to 20 bytes
os.ftruncate(f.fileno(), 20)
# Show extended content
f.seek(0)
extended_content = f.read()
print(f"Extended content: '{extended_content}'")
print(f"Extended content length: {len(extended_content)}")
print(f"Extended content as bytes: {extended_content.encode()}")
The code creates a small file, then extends it to 20 bytes. The new space is filled with null bytes (\x00), which may not be visible when printed as text.
When reading the extended file, the null bytes are included in the content but may not display visibly in terminal output depending on your environment.
Truncating to Zero
A common use case is truncating a file to zero bytes, effectively clearing its content while keeping the file intact. This example shows how to do this.
import os
# Create a file with content
with open("logfile.log", "w") as f:
f.write("This is some log content\n" * 10)
# Check original size
original_size = os.path.getsize("logfile.log")
print(f"Original log file size: {original_size} bytes")
# Truncate to zero
with open("logfile.log", "r+") as f:
os.ftruncate(f.fileno(), 0)
# Verify truncation
new_size = os.path.getsize("logfile.log")
print(f"After truncation: {new_size} bytes")
# File still exists but is empty
with open("logfile.log", "r") as f:
content = f.read()
print(f"File content: '{content}' (length: {len(content)})")
This example creates a log file, then truncates it to zero bytes. The file remains in place but contains no data. This is useful for log rotation.
Note that the file must be opened in a mode that allows writing (r+ in this case) for truncation to work. The file's metadata (like creation time) remains.
Error Handling
os.ftruncate can raise various exceptions. This example shows
proper error handling for common scenarios like permission issues.
import os
import errno
def safe_truncate(filename, size):
try:
with open(filename, "r+") as f:
os.ftruncate(f.fileno(), size)
print(f"Successfully truncated {filename} to {size} bytes")
except OSError as e:
if e.errno == errno.EBADF:
print(f"Error: Bad file descriptor for {filename}")
elif e.errno == errno.EINVAL:
print(f"Error: Invalid size {size} for {filename}")
elif e.errno == errno.EPERM:
print(f"Error: Permission denied for {filename}")
else:
print(f"Unexpected error: {e}")
# Test cases
safe_truncate("existing.txt", 100) # Assuming this file exists
safe_truncate("/root/protected.txt", 0) # Likely permission denied
safe_truncate("nonexistent.txt", 50) # FileNotFoundError
safe_truncate("valid.txt", -10) # Invalid size
This function demonstrates handling various errors that might occur during truncation. Different error numbers indicate different types of failures.
Note that attempting to truncate a non-existent file raises FileNotFoundError, which we catch as OSError in Python. Always handle potential errors when working with file operations.
Working with Binary Files
os.ftruncate works equally well with binary files. This example
shows truncating a binary file at specific positions.
import os
import struct
# Create a binary file with various data types
with open("data.bin", "wb") as f:
# Write different data types
f.write(struct.pack('i', 42)) # Integer
f.write(struct.pack('f', 3.14)) # Float
f.write(struct.pack('10s', b"binary")) # String
f.write(struct.pack('?', True)) # Boolean
# Check original size
original_size = os.path.getsize("data.bin")
print(f"Original binary file size: {original_size} bytes")
# Truncate after the integer and float (8 bytes)
with open("data.bin", "r+b") as f:
os.ftruncate(f.fileno(), 8)
# Read remaining content
f.seek(0)
data = f.read()
print(f"Remaining bytes: {data}")
print(f"Unpacked integer: {struct.unpack('i', data[:4])[0]}")
print(f"Unpacked float: {struct.unpack('f', data[4:8])[0]}")
This creates a binary file with multiple data types, then truncates it after the first two values. The remaining content is read and unpacked to verify.
Binary file truncation is precise since we're working with exact byte counts. This is useful when dealing with fixed-size records or headers in binary files.
Comparing with os.truncate
Python also provides os.truncate which works similarly but takes
a path instead of file descriptor. This example compares both functions.
import os
filename = "compare.txt"
# Create sample file
with open(filename, "w") as f:
f.write("This is a file for comparing truncation methods")
# Method 1: Using os.ftruncate with file descriptor
with open(filename, "r+") as f:
os.ftruncate(f.fileno(), 10)
f.seek(0)
fd_content = f.read()
print(f"os.ftruncate result: '{fd_content}'")
# Reset file
with open(filename, "w") as f:
f.write("This is a file for comparing truncation methods")
# Method 2: Using os.truncate with path
os.truncate(filename, 10)
with open(filename, "r") as f:
path_content = f.read()
print(f"os.truncate result: '{path_content}'")
# Compare
print(f"Results equal: {fd_content == path_content}")
Both methods achieve the same result but through different interfaces.
os.ftruncate requires an open file descriptor, while
os.truncate works with a path string.
The choice depends on context: use os.ftruncate when you already have a file open, and os.truncate when working with file paths directly.
Real-world Log Rotation
This example shows a practical application of os.ftruncate in
a log rotation system that limits log file size.
import os
import time
LOG_FILE = "app.log"
MAX_SIZE = 1024 # 1KB max log size
def write_log(message):
# Check current size
if os.path.exists(LOG_FILE):
current_size = os.path.getsize(LOG_FILE)
if current_size >= MAX_SIZE:
# Rotate log by truncating
with open(LOG_FILE, "r+") as f:
os.ftruncate(f.fileno(), 0)
print("Log rotated (truncated)")
# Append new log entry
with open(LOG_FILE, "a") as f:
timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
f.write(f"[{timestamp}] {message}\n")
# Simulate log writing
for i in range(100):
write_log(f"Event {i}: This is a log message")
time.sleep(0.1) # Small delay
# Show final log size
final_size = os.path.getsize(LOG_FILE)
print(f"Final log size: {final_size} bytes")
This log rotation system checks file size before each write. If the log exceeds 1KB, it's truncated to zero bytes. In production, you'd want more sophisticated rotation.
Real implementations might archive old logs instead of truncating, but this
demonstrates the core concept of size management using os.ftruncate.
Security Considerations
- File descriptors: Ensure valid descriptors are passed to os.ftruncate
- Permissions: Requires write permission on the target file
- Race conditions: File state may change between size check and truncation
- Data loss: Truncation permanently removes data beyond the new size
- Cross-platform: Behavior consistent across Unix and Windows
Best Practices
- Error handling: Always handle potential OSError exceptions
- File modes: Open files in appropriate modes (r+ or w) for truncation
- Backups: Consider creating backups before destructive operations
- Atomic operations: For critical files, consider safer alternatives
- Documentation: Clearly document truncation behavior in your code
Source References
Author
List all Python tutorials.