Python · Day 22

按编号删除与
文件重写

第二十二天,我为学习记录管理器增加了删除功能。程序不只修改运行时的列表,还会把剩余记录重新写进文件,让删除在下一次启动后依然有效。

用 enumerate() 自动显示编号

以前需要手动准备 number = 1 并在每轮循环后加一。今天使用 enumerate() 同时取得编号和记录,start=1 让用户看到的编号直接从 1 开始。

day22.py
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()

day22.py
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" 清空旧内容,再逐条写回仍然保留的记录。

day22.py
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。异常被接住后,删除函数根本不会调用,文件也不会发生改变。

day22.py
try:                                             # 尝试把用户输入转换成整数
    number = int(input("请输入记录编号:"))          # 获取用户选择的显示编号
except ValueError:                               # 输入不是整数时进入这里
    print("输入错误,请输入整数编号")               # 提示后不执行任何删除操作
else:                                            # 转换成功后才进入删除流程
    result = delete_note(notes_result, number)       # 删除记录并重写文件
    print(result)                                    # 显示成功或编号不存在

接入五功能循环菜单

完整程序现在有添加、查看、搜索、删除和退出五个选项。选择删除时先重新读取文件,保证拿到最新记录;输入有效编号后才会修改列表和文件;功能结束后继续返回菜单。

day22.py
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" 重写剩余记录,才能永久保存删除结果
  • 长程序可以拆成主菜单和多个职责单一的函数来理解

今天的程序超过一百行,但核心并不复杂:菜单负责分配任务,函数分别负责读取、显示、搜索、删除和保存。把路线拆开后,每一部分仍然只是已经掌握的小步骤。