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 教程。