Python os.ftruncate 函数
上次修改时间:2025 年 4 月 11 日
本全面指南探讨了 Python 的 os.ftruncate 函数,该函数通过截断或扩展文件来修改文件大小。我们将介绍文件描述符、大小修改和实际的文件操作示例。
基本定义
os.ftruncate 函数更改由文件描述符引用的文件的大小。如果新大小大于当前大小,它可以截断文件或用空字节扩展文件。
关键参数:fd (文件描述符), length (以字节为单位的新大小)。需要对文件具有写权限。可在 Unix 和 Windows 系统上运行。
基本文件截断
此示例演示了使用 os.ftruncate 减小文件大小的最简单方法。我们首先创建一个包含一些内容的示例文件。
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()}'")
此代码创建一个文件,然后在读写模式下打开它。它显示原始大小,截断为 10 个字节,并显示新的大小和内容。
文件描述符使用 fileno() 从文件对象获取。截断后,该文件仅包含原始内容的前 10 个字节。
扩展文件
当新大小超过当前大小时,os.ftruncate 还可以通过添加空字节来扩展文件。此示例演示了此行为。
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()}")
此代码创建一个小文件,然后将其扩展到 20 个字节。新空间用空字节 (\x00) 填充,这些字节在作为文本打印时可能不可见。
读取扩展文件时,空字节包含在内容中,但可能不会在终端输出中可见,具体取决于您的环境。
截断为零
一个常见的用例是将文件截断为零字节,从而有效地清除其内容,同时保持文件不变。此示例展示了如何执行此操作。
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)})")
此示例创建一个日志文件,然后将其截断为零字节。该文件保留在原位,但不包含任何数据。这对于日志轮换很有用。
请注意,必须以允许写入的模式(在本例中为 r+)打开文件,才能进行截断。文件的元数据(如创建时间)保持不变。
错误处理
os.ftruncate 可能会引发各种异常。此示例展示了针对常见场景(如权限问题)的正确错误处理。
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
此函数演示了处理截断期间可能发生的各种错误。不同的错误编号指示不同类型的故障。
请注意,尝试截断不存在的文件会引发 FileNotFoundError,我们在 Python 中将其捕获为 OSError。在使用文件操作时,始终处理潜在的错误。
处理二进制文件
os.ftruncate 对二进制文件的处理效果同样出色。此示例显示了在特定位置截断二进制文件。
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]}")
这将创建一个包含多种数据类型的二进制文件,然后在前两个值之后截断它。剩余的内容被读取和解包以进行验证。
二进制文件截断是精确的,因为我们处理的是精确的字节数。这在处理二进制文件中的固定大小的记录或标头时非常有用。
与 os.truncate 比较
Python 还提供了 os.truncate,它的工作方式类似,但采用路径而不是文件描述符。此示例比较了这两个函数。
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}")
这两种方法都通过不同的接口实现了相同的结果。 os.ftruncate 需要一个打开的文件描述符,而 os.truncate 使用路径字符串。
选择取决于上下文:当您已经打开一个文件时,使用 os.ftruncate;当直接处理文件路径时,使用 os.truncate。
真实世界的日志轮换
此示例展示了 os.ftruncate 在限制日志文件大小的日志轮换系统中的实际应用。
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")
此日志轮换系统在每次写入之前检查文件大小。如果日志超过 1KB,它将被截断为零字节。在生产环境中,您需要更复杂的轮换。
实际的实现可能会存档旧日志而不是截断,但这演示了使用 os.ftruncate 进行大小管理的核心概念。
安全注意事项
- 文件描述符: 确保将有效描述符传递给 os.ftruncate
- 权限: 需要对目标文件具有写权限
- 竞争条件: 文件状态可能在大小检查和截断之间发生变化
- 数据丢失: 截断会永久删除超出新大小的数据
- 跨平台: 在 Unix 和 Windows 上的行为一致
最佳实践
- 错误处理: 始终处理潜在的 OSError 异常
- 文件模式: 以适当的模式 (r+ 或 w) 打开文件以进行截断
- 备份: 在进行破坏性操作之前,请考虑创建备份
- 原子操作: 对于关键文件,请考虑更安全的替代方案
- 文档: 在代码中清楚地记录截断行为
资料来源
作者
列出所有 Python 教程。