文件操作插画

上一课的"学生成绩小管家"做得挺像样,可它有个大毛病:程序一关,辛辛苦苦录入的成绩全没了,下次打开又得从零录起。这一课给它装上"记忆"——把数据写进文件,关了程序、关了电脑,数据都还在。

一、为什么需要文件

之前我们用的变量,都是把数据存在电脑的内存里,好比记在脑子里:一断电(程序结束),忘得干干净净。而文件是存在硬盘上的,好比记在笔记本上:本子合上,明天翻开还在。

真实软件全是这么干的:微信聊天记录、游戏存档、你打的文档,本质上都是"把数据写进文件"。学会文件操作,你的程序才算从玩具变成工具。

二、写文件:三步走

把一句话存进文件,只需要三步:打开 → 写入 → 关闭。

with open("note.txt", "w", encoding="utf-8") as f:
    f.write("今天学会了文件操作!\n")
    f.write("程序关掉,数据还在。\n")

运行完,程序目录里会多出一个 note.txt,双击打开就是这两行字。逐个拆解:

部分意思
"note.txt"文件名。文件会生成在程序所在的文件夹里
"w"模式:w 是 write(写入)。文件不存在就新建,存在就清空重写
encoding="utf-8"按 utf-8 编码存,中文才不会变乱码。这行别省,省了迟早吃乱码的亏
f.write(...)把字符串写进文件。想换行要自己加 \n,程序不会自动帮你换

最关键的是开头的 with:它管着这个文件,里面的代码执行完,自动帮你关闭文件。文件开着不关,轻则数据没存全,重则文件损坏。以后写文件,一律用 with 开头,就不用操心关门的事了——好比出门不用记着锁门,门会自己锁。

三、读文件:把数据拿回来

读回来是反过来,把模式换成 "r"(read):

with open("note.txt", "r", encoding="utf-8") as f:
    text = f.read()
print(text)

read() 一次性把整个文件读成一个大字符串。还有个常用的 readlines(),按行读成列表,一行一个元素——不过每行末尾都带着换行符 \n,用之前一般要先 strip() 把它削掉,这个坑下一节就会撞上。

再认识一个模式 "a"(append,追加):

模式干什么文件已存在时
"r"读文件不存在会报错
"w"写先清空,再写
"a"追加写在末尾接着写,不清空

记日记、记账本这种"只往后加"的场景就用 "a";要整体重存(比如成绩表),用 "w"。

四、给小管家装上记忆

思路很清晰,就加两个函数:

函数职责
load_data()程序一启动,从 scores.txt 把成绩读回列表
save_data()每次录入后,把整个列表写回 scores.txt

存的时候要定个格式:一行存一个学生,姓名和成绩用逗号隔开,像这样:

小明,85
小红,92
小刚,78

这种"逗号分隔数据"的存法有个专门的名字叫 CSV,Excel 都能直接打开它,以后还会经常见到。读的时候用 split(",") 把一行拆回姓名和成绩就行。

完整代码(在第十一课的基础上加了两个函数,标★的是新内容):

import os

students = []   # 全班数据:一个装字典的列表
FILENAME = "scores.txt"

def save_data():   # ★ 保存:整个列表写回文件
    with open(FILENAME, "w", encoding="utf-8") as f:
        for s in students:
            f.write(f"{s['姓名']},{s['成绩']}\n")

def load_data():   # ★ 加载:启动时把文件读回列表
    if not os.path.exists(FILENAME):
        return   # 第一次运行,还没有这个文件,很正常
    with open(FILENAME, "r", encoding="utf-8") as f:
        for line in f:
            line = line.strip()          # 削掉行尾的换行符
            if not line:
                continue                 # 空行跳过
            name, score = line.split(",")
            students.append({"姓名": name, "成绩": float(score)})
    print(f"已从文件读入 {len(students)} 条成绩")

def add_student():
    name = input("姓名:")
    score = float(input("成绩:"))
    students.append({"姓名": name, "成绩": score})
    save_data()   # ★ 每录入一个就存一次,程序意外关掉也不丢
    print("录入成功!")

def show_all():
    if not students:
        print("还没有数据,先录入吧")
        return
    for s in students:
        print(f"{s['姓名']}:{s['成绩']}分")

def show_avg():
    if not students:
        print("还没有数据,先录入吧")
        return
    avg = sum(s["成绩"] for s in students) / len(students)
    print(f"全班平均分:{avg:.1f}")

def show_best():
    if not students:
        print("还没有数据,先录入吧")
        return
    best = max(students, key=lambda s: s["成绩"])
    print(f"最高分:{best['姓名']},{best['成绩']}分")

load_data()   # ★ 程序启动先加载旧数据
while True:
    print("1.录入成绩 2.查看全部 3.平均分 4.最高分 0.退出")
    choice = input("请选择:")
    if choice == "1":
        add_student()
    elif choice == "2":
        show_all()
    elif choice == "3":
        show_avg()
    elif choice == "4":
        show_best()
    elif choice == "0":
        print("再见!")
        break
    else:
        print("没有这个选项,重新选吧")

试两遍:第一遍录入两个学生然后退出,去程序目录看看 scores.txt 在不在;第二遍重新运行——"已从文件读入 2 条成绩",选 2 查看,数据原封不动地回来了。这就是"记忆"。

五、文件找不到怎么办

读一个不存在的文件,程序会直接报错崩掉(FileNotFoundError)。上面的 load_data 用 os.path.exists 先问一句"在不在",是办法之一。再教你一个更通用的招——try/except,翻译过来就是"试着干,出错了走这边":

try:
    with open("diary.txt", "r", encoding="utf-8") as f:
        print(f.read())
except FileNotFoundError:
    print("文件还不存在,先写一点内容进去吧")

程序先执行 try 里的代码;一旦报 FileNotFoundError,不崩,转去执行 except 里的代码。这招叫异常处理,以后还会反复见到,这里先混个脸熟。

本课小结

· 变量存内存,关机就忘;文件存硬盘,关机还在。
· 写文件用 with open(...) as f,自动关闭,不怕忘关门。
· 三种常用模式:"r" 读、"w" 清空写、"a" 追加写;中文记得带 encoding="utf-8"。
· 一行一条、逗号分隔,就是 CSV 的存法;读回来用 split 拆开,别忘 strip 削换行符。
· 文件不存在会报错:可以先 exists 判断,也可以用 try/except 兜底。

练一练

1. 通讯录:把 3 个同学的姓名和电话(一行一个,逗号分隔)存进 contacts.txt,再写个程序把它读出来打印。
2. 记账本:用追加模式 "a",每次运行输入一笔消费(比如"奶茶,15")追加进账本,并把全部记录打印出来看看。
3. 挑战题:给小管家加一个功能"5.删除学生":输入姓名,把这个学生从列表里删掉并保存。想想:删完如果不同步保存,会发生什么?

下一课我们把字符串"玩透":切片和各种常用方法——处理文本数据的看家本领。

More

相关教程