实验4.文件与异常综合实验
文件与异常综合实验
本实验把文本读写、with 上下文管理、异常处理、JSON 序列化、CSV 读写串成一个可持久化的成绩册:程序退出后数据仍在,再次打开可继续使用。
实验目标
- 会用
open+with读写文本,理解r/w/a的区别。 - 会用
try/except/else/finally处理文件不存在、格式错误。 - 会用
json.dump/json.load保存和恢复字典结构。 - 会用
csv.writer/csv.reader导出、导入表格数据。 - 能把实验3中的统计函数复用到文件数据上。
文件规划
| 文件 | 作用 |
|---|---|
students.json |
主数据,字典列表,程序启动时加载 |
report.txt |
文本报告,给人看 |
scores.csv |
表格导出,方便用 Excel 打开 |
参考实现
"""
成绩册持久化
覆盖:文件读写、异常、JSON、CSV、with
"""
import csv
import json
JSON_PATH = 'students.json'
REPORT_PATH = 'report.txt'
CSV_PATH = 'scores.csv'
def load_students(path=JSON_PATH):
"""从 JSON 读取学生列表;文件不存在或格式错误时返回空列表。"""
try:
with open(path, 'r', encoding='utf-8') as file:
data = json.load(file)
except FileNotFoundError:
print(f'未找到 {path},将创建新的空成绩册。')
return []
except json.JSONDecodeError:
print(f'{path} 不是合法的 JSON,已忽略并重新开始。')
return []
except OSError as e:
print(f'读取文件失败:{e}')
return []
if not isinstance(data, list):
print('JSON 根节点应为列表,已忽略并重新开始。')
return []
return data
def save_students(students, path=JSON_PATH):
"""把学生列表写入 JSON;ensure_ascii=False 便于查看中文。"""
with open(path, 'w', encoding='utf-8') as file:
json.dump(students, file, ensure_ascii=False, indent=2)
print(f'已保存 {len(students)} 条记录到 {path}')
def add_student(students, name, score):
if not name or not name.strip():
raise ValueError('姓名不能为空')
score = float(score)
if score < 0 or score > 100:
raise ValueError('成绩必须在 0~100 之间')
students.append({
'name': name.strip(),
'score': score,
})
def average_score(students):
if not students:
return 0
return sum(s['score'] for s in students) / len(students)
def write_report(students, path=REPORT_PATH):
"""写入人类可读的文本报告。"""
with open(path, 'w', encoding='utf-8') as file:
file.write('成绩报告\n')
file.write('=' * 20 + '\n')
for student in students:
file.write(f"{student['name']}: {student['score']:.1f}\n")
file.write('-' * 20 + '\n')
file.write(f"人数: {len(students)}\n")
file.write(f"平均分: {average_score(students):.2f}\n")
print(f'报告已写入 {path}')
def export_csv(students, path=CSV_PATH):
with open(path, 'w', encoding='utf-8', newline='') as file:
writer = csv.writer(file)
writer.writerow(['姓名', '成绩'])
for student in students:
writer.writerow([student['name'], student['score']])
print(f'已导出 CSV:{path}')
def import_csv(path=CSV_PATH):
"""从 CSV 读取;表头不符或成绩非法时跳过该行。"""
students = []
try:
with open(path, 'r', encoding='utf-8', newline='') as file:
reader = csv.reader(file)
header = next(reader, None)
if header != ['姓名', '成绩']:
print('CSV 表头应为 姓名,成绩')
return []
for row in reader:
if len(row) != 2:
continue
name, raw_score = row
try:
score = float(raw_score)
except ValueError:
print(f'跳过非法成绩行:{row}')
continue
if 0 <= score <= 100:
students.append({'name': name, 'score': score})
except FileNotFoundError:
print(f'未找到 {path}')
return students
def append_log(message, path='op.log'):
"""演示追加模式 a:日志不会覆盖旧内容。"""
with open(path, 'a', encoding='utf-8') as file:
file.write(message + '\n')
def main():
students = load_students()
# 若是空册,写入几条示例,便于首次演示
if not students:
demo = [('张三', 95), ('李四', 82), ('王五', 67)]
for name, score in demo:
try:
add_student(students, name, score)
except ValueError as e:
print(f'添加 {name} 失败:{e}')
append_log('初始化示例数据')
# 故意触发并捕获:非法成绩
try:
add_student(students, '非法', 120)
except ValueError as e:
print(f'预期错误已捕获:{e}')
append_log(f'捕获非法输入:{e}')
save_students(students)
write_report(students)
export_csv(students)
# 重新导入验证往返一致
imported = import_csv(CSV_PATH)
print('CSV 回读条数:', len(imported))
print('JSON 回读条数:', len(load_students(JSON_PATH)))
print('平均分:', round(average_score(students), 2))
if __name__ == '__main__':
main()预期结果要点
首次运行会创建 students.json、report.txt、scores.csv、op.log。控制台应出现类似:
未找到 students.json,将创建新的空成绩册。
预期错误已捕获:成绩必须在 0~100 之间
已保存 3 条记录到 students.json
报告已写入 report.txt
已导出 CSV:scores.csv
CSV 回读条数:3
JSON 回读条数:3
平均分:81.33再次运行时不应再打印“未找到 students.json”,记录会从磁盘恢复。
扩展挑战
- 增加菜单:1 添加 2 列表 3 导出 4 导入 5 退出,并把选择写入
op.log。 - 用
'a'模式把每次统计结果追加到history.txt,对比w与a。 - 读取损坏的 JSON 时,除了提示,再自动备份坏文件为
students.json.bak。
知识点总结
- 打开模式:
r只读,w截断后写,a追加;写 CSV 时建议newline='',避免 Windows 下多出空行;文本文件务必指定encoding='utf-8'。 - with 上下文:
with open(...) as file:在离开代码块时自动关闭文件,即使出现异常也更安全,应优先于手动close()。 - 异常处理:
try放可能出错的代码;按具体异常分支except FileNotFoundError、json.JSONDecodeError、ValueError;else在无异常时执行;finally适合释放资源;业务校验失败用raise ValueError主动抛出,让调用方用except接住。 - JSON:Python 字典/列表与 JSON 对象/数组对应;
json.dump/json.load面向文件,dumps/loads面向字符串;ensure_ascii=False保留中文可读性;indent让文件便于人眼检查。 - CSV:标准库
csv模块;writerow一行记录,reader迭代得到列表;导入时要校验表头和字段数,脏数据应跳过并提示,而不是让整个程序崩溃。 - 持久化流程:启动加载 → 内存修改(列表/字典)→ 退出或变更后保存;统计类函数只依赖内存中的数据结构,不直接碰磁盘,职责更清晰。
- 易错点:用
w误覆盖整份数据;忘记encoding导致中文乱码;except过宽(裸except:)吞掉所有错误;JSON 根类型不对仍直接当列表用;CSV 没有处理空行或表头。 - 学习重点:能独立完成“读入—校验—修改—写回”的闭环,并知道哪些错误必须处理、哪些应让程序尽早失败。