断点续解析的关键是异常时主动保存偏移量或行号:逐行读存最后一行号,流式读存file.tell()位置,CSV迭代器需手动计数;用try-except捕获异常并在break前写入断点,或用finally确保落盘,同时避免变量误判和非seekable流误用tell()。

在流式文件解析(如逐行读取大日志、CSV 或自定义协议文本)过程中,若因异常(如格式错误、网络中断、内存不足)导致解析中断,想下次从中断处继续,关键不是靠 break 本身“记录位置”,而是用 break 配合异常处理逻辑,在退出前主动保存当前偏移量或行号。
明确“断点”的物理含义
断点位置取决于你的读取方式:
- 如果是
file.readline()逐行读:断点 = 当前已成功处理的**最后一行的行号**(即下一次应从line_num + 1开始) - 如果是
file.read(chunk_size)流式读:断点 = 文件指针当前位置(file.tell()),即下次调用file.seek(pos)后继续读 - 如果是用迭代器(如
csv.reader(f)):需配合手动计数或封装成可恢复的迭代器,因为原生迭代器不暴露内部位置
用 try-except 捕获异常,在 break 前写入断点
break 本身不能跨异常生效,但它可在 except 块中用于跳出循环,并在跳出前把当前状态存下来。例如:
def parse_stream_with_checkpoint(filename, checkpoint_file="checkpoint.txt"):
last_line_num = 0
with open(filename, "r", encoding="utf-8") as f:
# 尝试从上次断点恢复
try:
with open(checkpoint_file, "r") as cf:
last_line_num = int(cf.read().strip())
except (FileNotFoundError, ValueError):
pass
<pre class="brush:php;toolbar:false;"> for line_num, line in enumerate(f, start=1):
if line_num <p></p>更健壮的做法:用 finally 确保断点写入
如果解析逻辑复杂、有多个可能抛出异常的位置,把保存逻辑放在 finally 更安全,但要注意区分“正常结束”和“异常中断”:
def parse_with_finally(filename, checkpoint_file="checkpoint.txt"):
line_num = 0
interrupted = False
with open(filename, "r", encoding="utf-8") as f:
try:
# 加载断点
with open(checkpoint_file, "r") as cf:
line_num = int(cf.read().strip())
except (FileNotFoundError, ValueError):
pass
<pre class="brush:php;toolbar:false;"> for i, line in enumerate(f, start=1):
if i <p></p>避免常见陷阱
不要依赖循环变量在 except 中的值:Python 的 for 循环变量在异常后仍保留最后一次赋值,但容易误判。显式用计数器更可靠。
文件指针不可回退时慎用 tell():如从管道、HTTP 响应流读取,seek() 不可用,此时只能用行号或消息 ID 类逻辑断点。
断点文件要原子写入:避免写到一半崩溃导致损坏,可用临时文件 + os.replace() 替换。










