4. 文件指针与定位:tell()、seek() 与三种移动模式
文件指针这东西,说白了就是文件内部的一个「书签」。你读到哪里,它就跟到哪里。我刚开始学文件操作时,总觉得它很抽象,后来在做一个日志分析工具时,才真正体会到它的重要性。
当时我需要反复读取同一个文件的某一段内容。如果每次都要从头读到尾,那效率太低了。嗯,这时候文件指针的定位功能就派上了大用场。
4.1 tell() 方法——看看指针在哪儿
tell() 方法返回文件指针的当前位置。单位是字节。你想想看,就像你读书时手指指着的那个位置,tell() 就是告诉你手指现在在第几行。
with open('example.txt', 'r', encoding='utf-8') as f:
content = f.read(5) # 读取前5个字符
position = f.tell() # 看看指针现在在哪儿
print(f'当前指针位置:{position}')
我在项目中遇到过一个问题:用 tell() 获取位置后,想跳回去重新读。结果发现位置不对。后来才意识到,不同编码下,一个中文字符占的字节数不一样。UTF-8 下,一个中文占 3 个字节。所以 tell() 返回的字节数,和你肉眼看到的字符数,往往对不上。
4.2 seek() 方法——让指针跳到你想要的位置
seek() 方法用来移动文件指针。它的语法很简单:
file.seek(offset, whence)
offset 是偏移量,whence 是参考位置。我习惯把 whence 理解为「从哪儿开始算」。0 表示从文件开头算,1 表示从当前位置算,2 表示从文件末尾算。
举个例子:
with open('data.bin', 'rb') as f:
# 跳到第10个字节
f.seek(10, 0)
data = f.read(4)
print(data)
# 从当前位置往后跳5个字节
f.seek(5, 1)
data = f.read(4)
print(data)
# 从文件末尾往前跳8个字节
f.seek(-8, 2)
data = f.read(4)
print(data)
我曾经在做一个二进制文件解析器时,需要跳过文件头部的 64 字节元数据,直接读取数据区。用 seek(64, 0) 一步到位,省去了逐字节读取的麻烦。
4.3 三种移动模式(0/1/2)
这三种模式,说白了就是三种参考系。我整理了一张表,方便你对照:
| 模式 | 常量名 | 含义 | offset 取值 |
|---|---|---|---|
| 0 | os.SEEK_SET |
从文件开头计算偏移 | 必须为非负数 |
| 1 | os.SEEK_CUR |
从当前位置计算偏移 | 可以为负数(往前移) |
| 2 | os.SEEK_END |
从文件末尾计算偏移 | 通常为负数(往前移) |
为什么会这样?因为文本模式下,Python 会做一些换行符的转换(比如 Windows 下的 \r\n 转成 \n),导致字节数和实际位置对不上。所以 Python 干脆限制了文本模式下的 seek 行为。
4.4 实战:用文件指针实现断点续读
我记得有一次写一个日志监控程序,需要记录上次读到的位置,下次启动时接着读。这就是典型的断点续读场景。
import os
def read_log_with_resume(log_file, position_file):
# 读取上次记录的位置
if os.path.exists(position_file):
with open(position_file, 'r') as pf:
last_pos = int(pf.read().strip())
else:
last_pos = 0
# 从上次位置继续读取
with open(log_file, 'r', encoding='utf-8') as f:
f.seek(last_pos, 0)
new_content = f.read()
current_pos = f.tell()
# 记录当前位置
with open(position_file, 'w') as pf:
pf.write(str(current_pos))
return new_content
# 使用示例
new_logs = read_log_with_resume('app.log', 'position.txt')
print(new_logs)
这个例子中,我们用 seek() 跳到最后一次读取的位置,用 tell() 记录当前读取结束的位置。下次再调用时,就能无缝衔接。
4.5 知识体系总览
下面这张图,帮你把文件指针的核心逻辑串起来:
这张图把 tell()、seek() 和三种模式的关系画清楚了。你想想看,掌握了这三个东西,文件指针就完全在你掌控之中了。
- tell() 告诉你指针在哪儿,seek() 让指针去你想去的地方
- 三种模式 0/1/2 对应三种参考系:开头、当前位置、末尾
- 文本模式下 seek 有限制,二进制模式下才能自由移动
- 文件指针是实现断点续读、随机访问的关键
嗯,文件指针这部分就讲到这里。记住一句话:指针在哪儿,读写就在哪儿。搞清楚了指针的位置,文件操作就成功了一半。
可可精品资料库