按编号删除与
文件重写
第二十二天,我为学习记录管理器增加了删除功能。程序不只修改运行时的列表,还会把剩余记录重新写进文件,让删除在下一次启动后依然有效。
用 enumerate() 自动显示编号
以前需要手动准备 number = 1 并在每轮循环后加一。今天使用 enumerate() 同时取得编号和记录,start=1 让用户看到的编号直接从 1 开始。
def show_notes(notes): # 定义编号显示记录的函数
if len(notes) == 0: # 判断当前列表是否为空
print("暂时没有学习记录") # 空列表时向用户显示提示
return # 提前结束函数,不进入循环
for number, note in enumerate(notes, start=1): # 从 1 开始生成编号并取得记录
print(number, note) # 显示当前编号和记录内容
编号只是显示给用户看的。列表的真实索引仍然从 0 开始,所以用户输入编号 3 时,对应索引是 2。
把用户编号转换为列表索引
删除函数先计算 index = number - 1。接着检查索引是否小于 0,或者是否大于等于列表长度,避免把不存在的编号交给 pop()。
def delete_note(notes, number): # 接收记录列表和用户选择的编号
index = number - 1 # 把从 1 开始的编号转换成索引
if index < 0 or index >= len(notes): # 判断索引是否超出列表范围
return "删除失败,记录编号不存在" # 编号无效时立即结束函数
deleted_note = notes.pop(index) # 删除元素并接住被删除的内容
save_all_notes(notes) # 把剩余记录重新保存到文件
return "已删除:" + deleted_note # 返回本次成功删除的记录
测试编号 2 时,“学习文件”被删除,列表剩下“学习函数”和“学习菜单”;测试编号 5 时范围判断成立,函数提前返回,原列表保持不变。
列表删除不会自动修改文件
pop() 只改变内存里的列表。程序重新启动后还会读取硬盘中的旧文件,因此如果没有下一步,被删除的记录仍会出现。
永久删除的完整路线是:读取文件 → 得到列表 → 删除列表元素 → 用剩余列表重写整个文件。
用剩余列表重写整个文件
普通文本文件不能像列表一样直接删除中间一行。save_all_notes() 使用 "w" 清空旧内容,再逐条写回仍然保留的记录。
def save_all_notes(notes): # 接收删除后的剩余记录列表
with open("learning_note.txt", "w", encoding="utf-8") as file: # 清空旧文件并准备重写
for note in notes: # 逐条取得仍然保留的记录
file.write(note + "\n") # 重新写入记录并添加换行
如果这里误用 "a",旧内容不会被清空,剩余记录只会追加到末尾,反而产生重复内容。实际测试删除“删除功能测试”后,重新运行程序只显示原来的四条记录,说明文件已经真正更新。
异常输入不会误删记录
用户可能输入 abc,这时 int() 会产生 ValueError。异常被接住后,删除函数根本不会调用,文件也不会发生改变。
try: # 尝试把用户输入转换成整数
number = int(input("请输入记录编号:")) # 获取用户选择的显示编号
except ValueError: # 输入不是整数时进入这里
print("输入错误,请输入整数编号") # 提示后不执行任何删除操作
else: # 转换成功后才进入删除流程
result = delete_note(notes_result, number) # 删除记录并重写文件
print(result) # 显示成功或编号不存在
接入五功能循环菜单
完整程序现在有添加、查看、搜索、删除和退出五个选项。选择删除时先重新读取文件,保证拿到最新记录;输入有效编号后才会修改列表和文件;功能结束后继续返回菜单。
elif choice == "4": # 用户选择删除记录功能
notes_result = load_notes() # 删除前读取文件中的最新记录
if len(notes_result) == 0: # 判断当前是否没有记录
print("暂时没有可以删除的学习记录") # 空列表时不要求输入编号
else: # 有记录时继续删除流程
print("请选择要删除的记录:") # 提示用户查看记录编号
show_notes(notes_result) # 编号显示全部最新记录
number = int(input("请输入记录编号:")) # 获取需要删除的显示编号
result = delete_note(notes_result, number) # 删除记录并永久保存结果
print(result) # 显示本次删除结果
今天真正掌握的内容
enumerate(notes, start=1)可以同时得到显示编号和记录- 用户编号减 1,才能得到从 0 开始的列表索引
pop(index)会删除元素,也会返回被删除的内容- 删除前必须验证索引范围,非整数输入由
ValueError处理 - 列表中的删除不会自动修改硬盘里的文本文件
- 使用
"w"重写剩余记录,才能永久保存删除结果 - 长程序可以拆成主菜单和多个职责单一的函数来理解
今天的程序超过一百行,但核心并不复杂:菜单负责分配任务,函数分别负责读取、显示、搜索、删除和保存。把路线拆开后,每一部分仍然只是已经掌握的小步骤。