ZetCode

Python os.lseek 函数

上次修改时间:2025 年 4 月 11 日

本综合指南探讨了 Python 的 os.lseek 函数,该函数用于更改文件位置以便读取/写入。我们将介绍定位模式、偏移量处理以及实际的文件操作示例。

基本定义

os.lseek 函数更改文件描述符的文件位置。它是一个底层操作,类似于 C 库的 lseek。

关键参数:fd(文件描述符),pos(偏移量),whence(参考点:SEEK_SET,SEEK_CUR,SEEK_END)。返回新的绝对位置。

从文件开头定位

SEEK_SET 模式从文件开头定位。此示例演示了通过定位到绝对位置来读取文件的特定部分。

seek_from_start.py
import os

# Create a test file
with open("data.bin", "wb") as f:
    f.write(b"ABCDEFGHIJKLMNOPQRSTUVWXYZ")

# Open and seek
fd = os.open("data.bin", os.O_RDONLY)
os.lseek(fd, 10, os.SEEK_SET)  # Seek to 11th byte (0-based)
data = os.read(fd, 5)
print(f"Read from position 10: {data.decode()}")  # KLMNO

os.close(fd)

这会创建一个包含字母表的二进制文件,然后定位到位置 10。读取操作从“K”开始,并向前读取 5 个字节。

请记住,文件位置在 Python 中是从 0 开始的。SEEK_SET,pos=0 是文件开头。

从当前位置搜索

SEEK_CUR 模式相对于当前位置进行定位。这允许向前/向后移动,而无需知道绝对位置。

seek_from_current.py
import os

fd = os.open("data.bin", os.O_RDONLY)

# Initial read
data = os.read(fd, 5)
print(f"First 5 bytes: {data.decode()}")  # ABCDE

# Seek forward 5 bytes
os.lseek(fd, 5, os.SEEK_CUR)
data = os.read(fd, 5)
print(f"Next 5 bytes: {data.decode()}")  # KLMNO

# Seek backward 10 bytes
os.lseek(fd, -10, os.SEEK_CUR)
data = os.read(fd, 5)
print(f"Rewound 10 bytes: {data.decode()}")  # FGHIJ

os.close(fd)

这显示了从当前位置向前和向后定位。第二个 seek 从当前位置向后移动 10 个字节。

负偏移量向后移动,正偏移量向前移动。该位置不能在文件开头之前。

从文件末尾定位

SEEK_END 模式相对于文件末尾进行定位。这对于追加或读取文件末尾而无需知道其确切大小非常有用。

seek_from_end.py
import os

fd = os.open("data.bin", os.O_RDONLY)

# Get file size by seeking to end
file_size = os.lseek(fd, 0, os.SEEK_END)
print(f"File size: {file_size} bytes")  # 26

# Read last 5 bytes
os.lseek(fd, -5, os.SEEK_END)
data = os.read(fd, 5)
print(f"Last 5 bytes: {data.decode()}")  # VWXYZ

# Seek beyond end (creates 'hole' in sparse files)
new_pos = os.lseek(fd, 100, os.SEEK_END)
print(f"New position: {new_pos}")  # 126

os.close(fd)

这演示了通过定位到末尾来获取文件大小、读取最后几个字节以及定位到超出末尾(这可能会创建稀疏文件)。

定位到超出末尾不会立即分配空间。该文件看起来更大,但在数据写入特定位置之前不会使用磁盘空间。

修改文件内容

将 lseek 与写入操作相结合可以实现精确的文件修改。此示例显示了就地更新,而无需重写整个文件。

modify_file.py
import os

# Create initial file
with open("records.dat", "wb") as f:
    f.write(b"RECORD1DATAXXXXX")

# Open for read-write
fd = os.open("records.dat", os.O_RDWR)

# Update specific record
os.lseek(fd, 7, os.SEEK_SET)  # Position after "RECORD1"
os.write(fd, b"UPDATED")

# Verify change
os.lseek(fd, 0, os.SEEK_SET)
data = os.read(fd, 20)
print(f"Modified content: {data.decode()}")  # RECORD1UPDATEDXX

os.close(fd)

这会创建一个带有占位符数据的文件,然后定位到位置 7 以覆盖部分内容。文件的其余部分保持不变。

对于固定长度的记录文件,lseek 提供了高效的随机访问,而无需加载整个文件。

处理大文件

os.lseek 支持大文件(>2GB),通过返回和接受 64 位偏移量。此示例演示了处理大文件位置。

large_files.py
import os

# Create a large sparse file (doesn't use actual disk space)
fd = os.open("large.bin", os.O_WRONLY | os.O_CREAT, 0o644)
os.lseek(fd, 2**32, os.SEEK_SET)  # 4GB position
os.write(fd, b"END")
os.close(fd)

# Verify the large file
fd = os.open("large.bin", os.O_RDONLY)
size = os.lseek(fd, 0, os.SEEK_END)
print(f"File size: {size} bytes")  # 4294967299 (4GB + 3 bytes)

# Read the written data
os.lseek(fd, 2**32, os.SEEK_SET)
data = os.read(fd, 3)
print(f"Data at 4GB offset: {data.decode()}")  # END

os.close(fd)

这会创建一个在 4GB 偏移量处具有数据的稀疏文件。在将数据写入特定位置之前,实际磁盘使用量非常小。

在大多数现代系统中,Python 的 os.lseek 支持最大 2^63-1 字节(8艾字节)的文件大小。

查找当前位置

使用偏移量 0 和 SEEK_CUR 调用 lseek 会返回当前位置,而不会更改它。这对于位置跟踪非常有用。

current_position.py
import os

fd = os.open("data.bin", os.O_RDONLY)

# Initial position
pos = os.lseek(fd, 0, os.SEEK_CUR)
print(f"Initial position: {pos}")  # 0

# Read some data
os.read(fd, 10)

# Check new position
pos = os.lseek(fd, 0, os.SEEK_CUR)
print(f"After reading 10 bytes: {pos}")  # 10

# Seek and verify
os.lseek(fd, 5, os.SEEK_SET)
pos = os.lseek(fd, 0, os.SEEK_CUR)
print(f"After seeking to 5: {pos}")  # 5

os.close(fd)

这演示了在操作之间获取当前位置。无论之前的 seek 或读取操作如何,该技术都有效。

这在实现需要跟踪位置的复杂文件解析算法时特别有用。

错误处理

os.lseek 可能会为无效操作引发 OSError。此示例显示了针对各种边缘情况的正确错误处理。

error_handling.py
import os

try:
    # Invalid file descriptor
    os.lseek(999, 0, os.SEEK_SET)
except OSError as e:
    print(f"Error with bad fd: {e}")

try:
    # Seek before start
    fd = os.open("data.bin", os.O_RDONLY)
    os.lseek(fd, -1, os.SEEK_SET)
except OSError as e:
    print(f"Error seeking before start: {e}")
finally:
    os.close(fd)

try:
    # Directory seek (invalid)
    fd = os.open(".", os.O_RDONLY)
    os.lseek(fd, 10, os.SEEK_SET)
except OSError as e:
    print(f"Error seeking in directory: {e}")
finally:
    os.close(fd)

这显示了三种常见的错误情况:无效的描述符、无效的偏移量以及在目录中定位(不支持)。

在使用 lseek 等底层文件操作时,始终处理潜在的 OSError 异常。

安全注意事项

最佳实践

资料来源

作者

我叫 Jan Bodnar,是一位充满热情的程序员,拥有丰富的编程经验。 自 2007 年以来,我一直在撰写编程文章。 迄今为止,我撰写了超过 1,400 篇文章和 8 本电子书。 我拥有超过十年的编程教学经验。

列出所有 Python 教程