Python 文件和异常练习题(含答案)
1. 创建并读取文件
题目: 编写程序完成以下操作:
- 将下面的内容写入文件
greeting.txt - 重新读取文件,打印全部内容
- 再次读取文件,打印总行数
你好,Python!
学习编程很有趣。
坚持下去,一定能成功!
参考答案:
# 第一步:写入文件
content = "你好,Python!n学习编程很有趣。n坚持下去,一定能成功!n"
with open("greeting.txt", "w", encoding="utf-8") as f:
f.write(content)
print("文件写入成功!")
# 第二步:读取全部内容
with open("greeting.txt", "r", encoding="utf-8") as f:
text = f.read()
print("文件内容:")
print(text)
# 第三步:统计行数
with open("greeting.txt", "r", encoding="utf-8") as f:
lines = f.readlines()
print(f"共 {len(lines)} 行")
输出:
文件写入成功!
文件内容:
你好,Python!
学习编程很有趣。
坚持下去,一定能成功!
共 3 行
关键点:
"w"模式写入,"r"模式读取,始终加encoding="utf-8"readlines()返回列表,每个元素是一行(含n),len()可得行数
2. 逐行读取并处理
题目: 创建文件 scores.txt,内容如下(每行一个分数):
85
92
67
78
95
43
88
编写程序读取该文件,计算并打印:最高分、最低分、平均分(保留1位小数)。
参考答案:
# 先创建文件
with open("scores.txt", "w", encoding="utf-8") as f:
f.write("85n92n67n78n95n43n88n")
# 读取并计算
scores = []
with open("scores.txt", "r", encoding="utf-8") as f:
for line in f: # 逐行读取,内存友好
line = line.strip() # 去掉换行符和空格
if line: # 跳过空行
scores.append(int(line)) # 转成整数
print(f"共 {len(scores)} 个分数:{scores}")
print(f"最高分:{max(scores)}")
print(f"最低分:{min(scores)}")
print(f"平均分:{sum(scores) / len(scores):.1f}")
输出:
共 7 个分数:[85, 92, 67, 78, 95, 43, 88]
最高分:95
最低分:43
平均分:78.3
关键点:
line.strip():同时去掉首尾空格和换行符if line::跳过空行,避免int("")报错
3. 追加写入文件
题目: 模拟一个简单的日志系统:
- 第一次运行时,写入"日志系统启动"
- 之后三次调用
add_log(message)函数,每次追加一条带序号的日志 - 最终读取并打印所有日志
参考答案:
LOG_FILE = "app.log"
# 初始化日志文件(覆盖写入)
with open(LOG_FILE, "w", encoding="utf-8") as f:
f.write("=== 日志系统启动 ===n")
def add_log(message, count=[0]):
"""追加一条日志(count 用列表保存状态,每次调用自增)"""
count[0] += 1
log_line = f"[{count[0]:03d}] {message}n"
with open(LOG_FILE, "a", encoding="utf-8") as f: # "a" 追加模式
f.write(log_line)
print(f"日志已记录:{log_line.strip()}")
# 追加三条日志
add_log("用户张三登录成功")
add_log("查询了订单列表")
add_log("用户张三退出登录")
# 读取所有日志
print("n=== 完整日志 ===")
with open(LOG_FILE, "r", encoding="utf-8") as f:
print(f.read())
输出:
日志已记录:[001] 用户张三登录成功
日志已记录:[002] 查询了订单列表
日志已记录:[003] 用户张三退出登录
=== 完整日志 ===
=== 日志系统启动 ===
[001] 用户张三登录成功
[002] 查询了订单列表
[003] 用户张三退出登录
关键点: "w" 初始化(清空),"a" 追加(保留原内容)。两者混淆会导致数据丢失。
4. 捕获文件不存在异常
题目: 编写函数 read_file(filename),尝试读取文件并返回内容:
- 文件不存在时:打印友好提示,返回
None - 编码错误时:提示用户可能不是 UTF-8 文件,返回
None - 成功时:返回文件内容字符串
测试:分别传入一个存在的文件名和一个不存在的文件名。
参考答案:
def read_file(filename):
"""安全读取文件,失败时返回 None"""
try:
with open(filename, "r", encoding="utf-8") as f:
content = f.read()
except FileNotFoundError:
print(f"[提示] 文件 '{filename}' 不存在,请检查路径是否正确。")
return None
except UnicodeDecodeError:
print(f"[提示] '{filename}' 可能不是 UTF-8 编码,请转换编码后再试。")
return None
else:
# 只有 try 正常执行完才会进入 else
print(f"[成功] 已读取文件 '{filename}',共 {len(content)} 个字符。")
return content
# 先创建一个测试文件
with open("test.txt", "w", encoding="utf-8") as f:
f.write("这是测试内容。n你好!")
# 测试
result1 = read_file("test.txt") # 存在的文件
result2 = read_file("not_exist.txt") # 不存在的文件
print("n读取结果:")
print(f"result1 = {repr(result1)}")
print(f"result2 = {repr(result2)}")
输出:
[成功] 已读取文件 'test.txt',共 13 个字符。
[提示] 文件 'not_exist.txt' 不存在,请检查路径是否正确。
读取结果:
result1 = '这是测试内容。n你好!'
result2 = None
关键点:
except FileNotFoundError:专门处理文件不存在except UnicodeDecodeError:专门处理编码错误else块:只有try完全成功时才执行,比把成功代码放在try里更清晰
5. try / except / else / finally 完整结构
题目: 编写函数 safe_divide_from_file(filename):
- 从文件中读取一行,格式为
"数字1 / 数字2"(如"10 / 2") - 解析并计算除法结果
- 使用
try/except/else/finally完整结构处理所有可能的异常 finally块打印"操作结束"
参考答案:
def safe_divide_from_file(filename):
try:
with open(filename, "r", encoding="utf-8") as f:
line = f.readline().strip() # 读取第一行
parts = line.split("/") # 按 / 分割
a = float(parts[0].strip())
b = float(parts[1].strip())
result = a / b
except FileNotFoundError:
print(f"[错误] 文件 '{filename}' 不存在")
return None
except (ValueError, IndexError):
print(f"[错误] 文件格式不正确,应为 '数字1 / 数字2',实际内容:'{line}'")
return None
except ZeroDivisionError:
print(f"[错误] 除数为零,无法计算")
return None
else:
print(f"[成功] {a} / {b} = {result:.4f}")
return result
finally:
print("操作结束") # 无论如何都执行
# 测试 1:正常情况
with open("math.txt", "w", encoding="utf-8") as f:
f.write("10 / 4")
safe_divide_from_file("math.txt")
print()
# 测试 2:除数为零
with open("math_zero.txt", "w", encoding="utf-8") as f:
f.write("10 / 0")
safe_divide_from_file("math_zero.txt")
print()
# 测试 3:文件不存在
safe_divide_from_file("no_file.txt")
输出:
[成功] 10.0 / 4.0 = 2.5000
操作结束
[错误] 除数为零,无法计算
操作结束
[错误] 文件 'no_file.txt' 不存在
操作结束
关键点: finally 块无论是否发生异常、是否有 return,都会执行。
6. 主动触发异常(raise)
题目: 编写函数 create_user(username, age, email):
username:长度必须在 3~20 个字符之间,否则raise ValueErrorage:必须在 1~120 之间,否则raise ValueErroremail:必须包含@,否则raise ValueError
测试函数,分别传入合法和非法的参数。
参考答案:
def create_user(username, age, email):
"""创建用户,对参数做合法性校验"""
# 校验用户名
if not (3 <= len(username) <= 20):
raise ValueError(f"用户名长度应在 3~20 个字符之间,当前长度:{len(username)}")
# 校验年龄
if not (1 <= age <= 120):
raise ValueError(f"年龄应在 1~120 之间,当前值:{age}")
# 校验邮箱
if "@" not in email:
raise ValueError(f"邮箱格式不正确,应包含 '@',当前值:'{email}'")
# 通过所有校验
user = {"username": username, "age": age, "email": email}
print(f"用户创建成功:{user}")
return user
# 测试
test_cases = [
("张三", 25, "zs@example.com"), # 合法
("ab", 25, "zs@example.com"), # 用户名太短
("张三", -5, "zs@example.com"), # 年龄非法
("张三", 25, "not_an_email"), # 邮箱格式错误
]
for username, age, email in test_cases:
try:
create_user(username, age, email)
except ValueError as e:
print(f"创建失败:{e}")
输出:
用户创建成功:{'username': '张三', 'age': 25, 'email': 'zs@example.com'}
创建失败:用户名长度应在 3~20 个字符之间,当前长度:2
创建失败:年龄应在 1~120 之间,当前值:-5
创建失败:邮箱格式不正确,应包含 '@',当前值:'not_an_email'
关键点: raise ValueError("描述信息") 主动触发异常,让调用者知道哪里出了问题,而不是让程序用错误数据继续执行。
7. 自定义异常类
题目: 为一个购物车系统定义两个自定义异常:
OutOfStockError(库存不足):携带商品名和当前库存量InvalidQuantityError(数量非法):携带传入的非法数量
编写 add_to_cart(product, stock, quantity) 函数使用这两个异常,并测试。
参考答案:
class OutOfStockError(Exception):
"""库存不足异常"""
def __init__(self, product, stock):
self.product = product
self.stock = stock
super().__init__(f"商品 '{product}' 库存不足,当前库存:{stock} 件")
class InvalidQuantityError(Exception):
"""数量非法异常"""
def __init__(self, quantity):
self.quantity = quantity
super().__init__(f"购买数量不合法:{quantity},必须是大于 0 的整数")
def add_to_cart(product, stock, quantity):
"""将商品添加到购物车"""
if not isinstance(quantity, int) or quantity <= 0:
raise InvalidQuantityError(quantity)
if quantity > stock:
raise OutOfStockError(product, stock)
print(f"成功将 {quantity} 件 '{product}' 加入购物车!(剩余库存:{stock - quantity})")
# 测试
test_cases = [
("苹果", 50, 3), # 正常
("香蕉", 5, 10), # 库存不足
("橙子", 20, -1), # 数量非法
("葡萄", 30, 0), # 数量非法(0也不行)
]
for product, stock, qty in test_cases:
try:
add_to_cart(product, stock, qty)
except OutOfStockError as e:
print(f"[库存不足] {e},您要买 {qty} 件")
except InvalidQuantityError as e:
print(f"[数量错误] {e}")
输出:
成功将 3 件 '苹果' 加入购物车!(剩余库存:47)
[库存不足] 商品 '香蕉' 库存不足,当前库存:5 件,您要买 10 件
[数量错误] 购买数量不合法:-1,必须是大于 0 的整数
[数量错误] 购买数量不合法:0,必须是大于 0 的整数
关键点: 自定义异常继承 Exception,在 __init__ 里保存有用的属性,并用 super().__init__(message) 设置错误消息。
8. 多级异常处理(找错题)
题目: 下面的代码有 3 处问题,找出并修复:
# 问题代码
def load_config(filename):
f = open(filename, encoding="utf-8") # 问题1
try:
content = f.read()
config = {}
for line in content.split("n"):
key, value = line.split("=") # 问题2:如果某行没有 = 号怎么办?
config[key.strip()] = value.strip()
return config
except: # 问题3
return {}
result = load_config("config.txt")
print(result)
参考答案(含问题说明):
# 问题1:open() 没有用 with 语句,发生异常时文件不会被关闭
# 问题2:如果某行没有 "=",split("=") 返回只有一个元素的列表,
# 解包给两个变量会报 ValueError,但没有处理这个情况
# 问题3:裸 except 太宽泛,连 KeyboardInterrupt、SystemExit 都会捕获,
# 应该用 except Exception 或更具体的类型
def load_config(filename):
try:
with open(filename, "r", encoding="utf-8") as f: # 修复1:使用 with
content = f.read()
except FileNotFoundError:
print(f"配置文件 '{filename}' 不存在,使用空配置。")
return {}
config = {}
for line_num, line in enumerate(content.split("n"), 1):
line = line.strip()
if not line or line.startswith("#"): # 跳过空行和注释行
continue
if "=" not in line: # 修复2:检查是否含 =
print(f"[警告] 第 {line_num} 行格式不正确,已跳过:'{line}'")
continue
key, value = line.split("=", 1) # maxsplit=1,允许 value 里含 =
config[key.strip()] = value.strip()
return config
# 创建测试配置文件
with open("config.txt", "w", encoding="utf-8") as f:
f.write("# 这是注释n")
f.write("name = Python学习n")
f.write("version = 1.0n")
f.write("这行没有等号n") # 格式错误的行
f.write("debug = truen")
result = load_config("config.txt")
print(result)
输出:
[警告] 第 4 行格式不正确,已跳过:'这行没有等号'
{'name': 'Python学习', 'version': '1.0', 'debug': 'true'}
9. 文件统计工具
题目: 编写函数 analyze_file(filename),统计一个文本文件的:
- 总行数(包括空行)
- 非空行数
- 总字符数(不含换行符)
- 最长的一行的长度和内容
用异常处理文件不存在的情况。
参考答案:
def analyze_file(filename):
try:
with open(filename, "r", encoding="utf-8") as f:
lines = f.readlines()
except FileNotFoundError:
print(f"文件 '{filename}' 不存在")
return
total_lines = len(lines)
non_empty = [l.rstrip("n") for l in lines if l.strip()]
non_empty_count = len(non_empty)
total_chars = sum(len(l.rstrip("n")) for l in lines)
if non_empty:
longest_line = max(non_empty, key=len)
longest_length = len(longest_line)
else:
longest_line, longest_length = "", 0
print(f"=== 文件分析:{filename} ===")
print(f"总行数: {total_lines}")
print(f"非空行数: {non_empty_count}")
print(f"总字符数: {total_chars}(不含换行符)")
print(f"最长行({longest_length} 字符):{longest_line}")
# 创建测试文件
with open("sample.txt", "w", encoding="utf-8") as f:
f.write("Python 是世界上最好的编程语言之一。n")
f.write("n")
f.write("它简洁、易读、功能强大。n")
f.write("适合新手入门,也适合专业开发。n")
f.write("n")
f.write("坚持学习,你一定能掌握 Python!n")
analyze_file("sample.txt")
analyze_file("nonexistent.txt")
输出:
=== 文件分析:sample.txt ===
总行数: 6
非空行数: 4
总字符数: 75(不含换行符)
最长行(18 字符):Python 是世界上最好的编程语言之一。
文件 'nonexistent.txt' 不存在
10. 用户输入验证(异常 + 循环)
题目: 编写程序,让用户输入一个 1~100 之间的整数:
- 如果输入的不是数字(如 "abc"),捕获
ValueError并提示重新输入 - 如果输入的是数字但不在 1~100 之间,提示重新输入
- 输入合法后,打印"输入有效:xx"并结束
参考答案:
def get_valid_number():
"""获取用户输入的 1~100 之间的整数,直到合法为止"""
while True:
raw = input("请输入 1~100 之间的整数:")
try:
number = int(raw)
except ValueError:
print(f" '{raw}' 不是整数,请重新输入。")
continue # 回到循环顶部,重新输入
if not (1 <= number <= 100):
print(f" {number} 不在 1~100 范围内,请重新输入。")
continue
# 到这里说明输入合法
print(f"输入有效:{number}")
return number
result = get_valid_number()
print(f"最终结果:{result}")
交互示例:
请输入 1~100 之间的整数:abc
'abc' 不是整数,请重新输入。
请输入 1~100 之间的整数:150
150 不在 1~100 范围内,请重新输入。
请输入 1~100 之间的整数:-5
-5 不在 1~100 范围内,请重新输入。
请输入 1~100 之间的整数:42
输入有效:42
最终结果:42
关键点: try/except + while True + continue 是处理用户输入校验的经典组合。
11. 文件备份工具
题目: 编写函数 backup_file(filename):
- 读取原文件内容
- 将内容写入备份文件(文件名 = 原文件名 +
.bak,如data.txt.bak) - 打印备份成功信息(包括原文件大小)
- 文件不存在时打印友好提示
- 备份文件已存在时,提示"将覆盖已有备份"
参考答案:
import os
def backup_file(filename):
"""备份文件到 filename.bak"""
backup_name = filename + ".bak"
# 读取原文件
try:
with open(filename, "r", encoding="utf-8") as f:
content = f.read()
except FileNotFoundError:
print(f"[失败] 原文件 '{filename}' 不存在,无法备份。")
return False
# 检查备份文件是否已存在
if os.path.exists(backup_name):
print(f"[提示] 备份文件 '{backup_name}' 已存在,将覆盖。")
# 写入备份文件
try:
with open(backup_name, "w", encoding="utf-8") as f:
f.write(content)
except PermissionError:
print(f"[失败] 没有权限写入备份文件 '{backup_name}'。")
return False
size = os.path.getsize(filename)
print(f"[成功] '{filename}'({size} 字节)已备份为 '{backup_name}'")
return True
# 测试
with open("important.txt", "w", encoding="utf-8") as f:
f.write("这是重要数据!n不能丢失!n")
backup_file("important.txt") # 第一次备份
backup_file("important.txt") # 第二次备份(覆盖提示)
backup_file("nonexistent.txt") # 文件不存在
输出:
[成功] 'important.txt'(26 字节)已备份为 'important.txt.bak'
[提示] 备份文件 'important.txt.bak' 已存在,将覆盖。
[成功] 'important.txt'(26 字节)已备份为 'important.txt.bak'
[失败] 原文件 'nonexistent.txt' 不存在,无法备份。
12. 自定义异常继承体系
题目: 为一个学生成绩管理系统设计异常体系:
GradeSystemError(基类):所有业务异常的父类ScoreRangeError(子类):分数超出范围(0~100)StudentNotFoundError(子类):学生不存在
编写 GradeBook 类,包含 add_student、add_score、get_score 三个方法,用上述异常保护数据合法性。
参考答案:
# ========== 自定义异常体系 ==========
class GradeSystemError(Exception):
"""成绩系统异常基类"""
pass
class ScoreRangeError(GradeSystemError):
"""分数超出范围异常"""
def __init__(self, score):
self.score = score
super().__init__(f"分数 {score} 超出合法范围(0~100)")
class StudentNotFoundError(GradeSystemError):
"""学生不存在异常"""
def __init__(self, name):
self.name = name
super().__init__(f"学生 '{name}' 不存在,请先添加")
# ========== 成绩本类 ==========
class GradeBook:
def __init__(self):
self._data = {} # {学生名: {科目: 分数}}
def add_student(self, name):
if name not in self._data:
self._data[name] = {}
print(f"已添加学生:{name}")
else:
print(f"学生 '{name}' 已存在")
def add_score(self, name, subject, score):
if name not in self._data:
raise StudentNotFoundError(name)
if not (0 <= score <= 100):
raise ScoreRangeError(score)
self._data[name][subject] = score
print(f"已录入:{name} - {subject}:{score} 分")
def get_score(self, name, subject):
if name not in self._data:
raise StudentNotFoundError(name)
scores = self._data[name]
if subject not in scores:
return None
return scores[subject]
# ========== 测试 ==========
gb = GradeBook()
gb.add_student("张三")
gb.add_student("李四")
# 正常录入
gb.add_score("张三", "数学", 92)
gb.add_score("张三", "英语", 85)
print()
# 测试各种异常
test_cases = [
("王五", "数学", 88), # 学生不存在
("张三", "数学", 110), # 分数超范围
("张三", "数学", -5), # 分数超范围
]
for name, subject, score in test_cases:
try:
gb.add_score(name, subject, score)
except StudentNotFoundError as e:
print(f"[学生错误] {e}")
except ScoreRangeError as e:
print(f"[分数错误] {e}")
except GradeSystemError as e:
print(f"[系统错误] {e}") # 捕获所有业务异常的兜底
print()
# 用 isinstance 验证继承关系
err = ScoreRangeError(110)
print(f"ScoreRangeError 是 GradeSystemError 的子类:{isinstance(err, GradeSystemError)}") # True
print(f"ScoreRangeError 是 Exception 的子类: {isinstance(err, Exception)}") # True
输出:
已添加学生:张三
已添加学生:李四
已录入:张三 - 数学:92 分
已录入:张三 - 英语:85 分
[学生错误] 学生 '王五' 不存在,请先添加
[分数错误] 分数 110 超出合法范围(0~100)
[分数错误] 分数 -5 超出合法范围(0~100)
ScoreRangeError 是 GradeSystemError 的子类:True
ScoreRangeError 是 Exception 的子类: True
13. 文件单词频率统计
题目: 编写程序,读取一个英文文本文件,统计出现频率最高的 10 个单词,忽略大小写,忽略标点符号。结果写入 word_count.txt。
参考答案:
import re
from collections import Counter
def count_words(input_file, output_file, top_n=10):
# 读取文本
try:
with open(input_file, "r", encoding="utf-8") as f:
text = f.read()
except FileNotFoundError:
print(f"[错误] 文件 '{input_file}' 不存在")
return
# 提取单词:转小写,用正则去掉非字母字符,按空白分词
words = re.findall(r"[a-z]+", text.lower())
counter = Counter(words)
top = counter.most_common(top_n)
# 写入结果
result_lines = [f"=== 词频统计(前 {top_n} 名)===n"]
for rank, (word, count) in enumerate(top, 1):
bar = "█" * count
line = f"{rank:2}. {word:15} {count:4} 次 {bar}n"
result_lines.append(line)
with open(output_file, "w", encoding="utf-8") as f:
f.writelines(result_lines)
print(f"统计完成,结果已写入 '{output_file}'")
for rank, (word, count) in enumerate(top, 1):
print(f" {rank:2}. {word:15} {count} 次")
# 创建示例英文文本文件
sample_text = """
Python is a great programming language. Python is easy to learn.
Python is used for web development, data science, and artificial intelligence.
Many developers love Python because Python code is readable and clean.
Learning Python is fun and Python community is very supportive.
"""
with open("english.txt", "w", encoding="utf-8") as f:
f.write(sample_text)
count_words("english.txt", "word_count.txt")
输出:
统计完成,结果已写入 'word_count.txt'
1. python 6 次
2. is 6 次
3. and 3 次
4. learning 1 次
5. ...
14. 综合题——简单通讯录(文件 + 异常 + JSON)
题目: 实现一个持久化通讯录,数据保存在 contacts.json,支持:
add_contact(name, phone, email):添加联系人(同名则更新)get_contact(name):查找联系人,不存在则触发自定义异常delete_contact(name):删除联系人,不存在则触发自定义异常show_all():显示所有联系人
参考答案:
import json
import os
CONTACTS_FILE = "contacts.json"
class ContactNotFoundError(Exception):
"""联系人不存在异常"""
def __init__(self, name):
super().__init__(f"联系人 '{name}' 不存在")
def _load():
"""加载通讯录数据"""
if not os.path.exists(CONTACTS_FILE):
return {}
try:
with open(CONTACTS_FILE, "r", encoding="utf-8") as f:
return json.load(f)
except json.JSONDecodeError:
print("[警告] 通讯录文件损坏,重置为空")
return {}
def _save(contacts):
"""保存通讯录数据"""
with open(CONTACTS_FILE, "w", encoding="utf-8") as f:
json.dump(contacts, f, ensure_ascii=False, indent=2)
def add_contact(name, phone, email):
contacts = _load()
action = "更新" if name in contacts else "新增"
contacts[name] = {"phone": phone, "email": email}
_save(contacts)
print(f"[{action}] {name}:{phone} / {email}")
def get_contact(name):
contacts = _load()
if name not in contacts:
raise ContactNotFoundError(name)
info = contacts[name]
print(f"[查找] {name}:电话 {info['phone']},邮箱 {info['email']}")
return info
def delete_contact(name):
contacts = _load()
if name not in contacts:
raise ContactNotFoundError(name)
del contacts[name]
_save(contacts)
print(f"[删除] 联系人 '{name}' 已删除")
def show_all():
contacts = _load()
if not contacts:
print("通讯录为空。")
return
print(f"n=== 通讯录(共 {len(contacts)} 人)===")
for name, info in contacts.items():
print(f" {name:10} 电话:{info['phone']:15} 邮箱:{info['email']}")
print()
# 测试
add_contact("张三", "13800001111", "zs@example.com")
add_contact("李四", "13900002222", "ls@example.com")
add_contact("王五", "15600003333", "ww@example.com")
add_contact("张三", "13800009999", "zs_new@example.com") # 更新张三
show_all()
try:
get_contact("李四")
get_contact("赵六") # 不存在,触发异常
except ContactNotFoundError as e:
print(f"[错误] {e}")
print()
try:
delete_contact("王五")
delete_contact("赵六") # 不存在,触发异常
except ContactNotFoundError as e:
print(f"[错误] {e}")
show_all()
输出:
[新增] 张三:13800001111 / zs@example.com
[新增] 李四:13900002222 / ls@example.com
[新增] 王五:15600003333 / ww@example.com
[更新] 张三:13800009999 / zs_new@example.com
=== 通讯录(共 3 人)===
张三 电话:13800009999 邮箱:zs_new@example.com
李四 电话:13900002222 邮箱:ls@example.com
王五 电话:15600003333 邮箱:ww@example.com
[查找] 李四:电话 13900002222,邮箱 ls@example.com
[错误] 联系人 '赵六' 不存在
[删除] 联系人 '王五' 已删除
[错误] 联系人 '赵六' 不存在
=== 通讯录(共 2 人)===
张三 电话:13800009999 邮箱:zs_new@example.com
李四 电话:13900002222 邮箱:ls@example.com
15. 综合挑战——学生成绩管理系统(文件持久化)
题目: 综合运用文件读写、JSON、异常处理和自定义异常,实现一个完整的学生成绩管理系统:
- 数据保存在
grade_book.json - 自定义异常:
StudentExistsError、StudentNotFoundError、InvalidScoreError - 功能:添加学生、录入成绩、查询学生报告、删除学生、显示全班报告
参考答案:
import json
import os
DB_FILE = "grade_book.json"
# ===== 自定义异常 =====
class StudentExistsError(Exception):
def __init__(self, name):
super().__init__(f"学生 '{name}' 已存在")
class StudentNotFoundError(Exception):
def __init__(self, name):
super().__init__(f"学生 '{name}' 不存在")
class InvalidScoreError(Exception):
def __init__(self, score):
super().__init__(f"分数 {score} 不合法,应在 0~100 之间")
# ===== 数据读写 =====
def _load():
if not os.path.exists(DB_FILE):
return {}
with open(DB_FILE, "r", encoding="utf-8") as f:
return json.load(f)
def _save(data):
with open(DB_FILE, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
# ===== 业务函数 =====
def add_student(name):
data = _load()
if name in data:
raise StudentExistsError(name)
data[name] = {}
_save(data)
print(f"✓ 学生 '{name}' 添加成功")
def add_score(name, subject, score):
if not (0 <= score <= 100):
raise InvalidScoreError(score)
data = _load()
if name not in data:
raise StudentNotFoundError(name)
data[name][subject] = score
_save(data)
print(f"✓ 已录入:{name} - {subject}:{score} 分")
def get_report(name):
data = _load()
if name not in data:
raise StudentNotFoundError(name)
scores = data[name]
print(f"n=== {name} 的成绩单 ===")
if not scores:
print(" 暂无成绩记录")
return
for subject, score in scores.items():
grade = "优" if score >= 90 else "良" if score >= 75 else "中" if score >= 60 else "差"
print(f" {subject:8}:{score:3} 分 [{grade}]")
avg = sum(scores.values()) / len(scores)
print(f" {'平均分':8}:{avg:.1f} 分")
def delete_student(name):
data = _load()
if name not in data:
raise StudentNotFoundError(name)
del data[name]
_save(data)
print(f"✓ 学生 '{name}' 已删除")
def class_report():
data = _load()
if not data:
print("暂无学生数据")
return
print(f"n{'='*45}")
print(f"{'全班成绩报告':^40}")
print(f"{'='*45}")
print(f"{'姓名':^8} {'平均分':^8} {'最高科目':^12}")
print("-" * 45)
for name, scores in data.items():
if scores:
avg = sum(scores.values()) / len(scores)
best_sub = max(scores, key=scores.get)
best_score = scores[best_sub]
print(f"{name:^8} {avg:^8.1f} {best_sub}({best_score}分)")
else:
print(f"{name:^8} {'无成绩':^8}")
print("=" * 45)
# ===== 测试 =====
# 添加学生
for name in ["张三", "李四", "王五"]:
try:
add_student(name)
except StudentExistsError as e:
print(f"[跳过] {e}")
# 录入成绩
score_data = [
("张三", "语文", 88), ("张三", "数学", 95), ("张三", "英语", 82),
("李四", "语文", 75), ("李四", "数学", 68), ("李四", "英语", 91),
("王五", "语文", 92), ("王五", "数学", 78), ("王五", "英语", 85),
]
print()
for name, subject, score in score_data:
try:
add_score(name, subject, score)
except (StudentNotFoundError, InvalidScoreError) as e:
print(f"[错误] {e}")
# 测试非法分数
print()
try:
add_score("张三", "物理", 120)
except InvalidScoreError as e:
print(f"[错误] {e}")
# 查询个人报告
get_report("李四")
# 全班报告
class_report()
# 删除学生
print()
try:
delete_student("王五")
delete_student("赵六") # 不存在
except StudentNotFoundError as e:
print(f"[错误] {e}")
class_report()
输出(节选):
✓ 学生 '张三' 添加成功
✓ 学生 '李四' 添加成功
✓ 学生 '王五' 添加成功
...
[错误] 分数 120 不合法,应在 0~100 之间
=== 李四 的成绩单 ===
语文 : 75 分 [良]
数学 : 68 分 [中]
英语 : 91 分 [优]
平均分 :78.0 分
=============================================
全班成绩报告
=============================================
姓名 平均分 最高科目
---------------------------------------------
张三 88.3 数学(95分)
李四 78.0 英语(91分)
王五 85.0 语文(92分)
=============================================
知识点速查
| 题号 | 考查点 |
|---|---|
| 1 | 写入文件 "w",读取 read()/readlines() |
| 2 | 逐行读取,.strip(),跳过空行 |
| 3 | 追加写入 "a","w" vs "a" 的区别 |
| 4 | try/except FileNotFoundError,else 块 |
| 5 | try/except/else/finally 完整结构 |
| 6 | raise ValueError 主动触发异常做参数校验 |
| 7 | 自定义异常类,携带属性,super().__init__() |
| 8 | 找错:with、格式校验、except Exception vs 裸 except |
| 9 | 文件统计分析,多个异常类型 |
| 10 | try/except + while True + continue 输入验证 |
| 11 | 文件备份,os.path.exists,PermissionError |
| 12 | 自定义异常继承体系,isinstance 验证 |
| 13 | re 提取单词,Counter 统计,writelines 写文件 |
| 14 | 综合:JSON 持久化通讯录 + 自定义异常 |
| 15 | 综合挑战:学生成绩管理系统(完整持久化) |