You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1081 lines
38 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import json
import csv
import os
from typing import List, Optional, Dict, Union, Any
from datetime import date, datetime
from abc import ABC, abstractmethod
from enum import Enum
import re
# ============== 数据模型层 ==============
class Student:
"""学生实体类,封装学生基本信息及相关操作"""
def __init__(self,
name: str,
id_card: str,
stu_id: str,
gender: Optional[bool] = None,
height: Optional[int] = None,
weight: Optional[float] = None,
enrollment_date: Optional[Union[date, str]] = None,
class_name: Optional[str] = None,
major: Optional[str] = None):
"""初始化学生对象"""
self.name = name
self.id_card = id_card
self.stu_id = stu_id
self.gender = gender
self.height = height
self.weight = weight
self.enrollment_date = enrollment_date
self.class_name = class_name
self.major = major
# 从身份证号生成的属性
self._birthday = None
self._age = None
# 错误信息字典
self._errors = {}
# 初始化时进行数据校验
self.validate()
@property
def birthday(self) -> date:
"""从身份证号提取出生日期"""
if not self._birthday and self.id_card and len(self.id_card) == 18:
birth_str = self.id_card[6:14]
try:
self._birthday = date.fromisoformat(birth_str)
except:
self._birthday = date(1900, 1, 1)
return self._birthday
@property
def age(self) -> int:
"""计算年龄"""
if not self._age and self.birthday:
today = date.today()
self._age = today.year - self.birthday.year
if today < date(today.year, self.birthday.month, self.birthday.day):
self._age -= 1
return self._age
@property
def enrollment_date(self) -> Optional[date]:
"""入学日期属性"""
return self._enrollment_date
@enrollment_date.setter
def enrollment_date(self, value: Optional[Union[date, str]]):
"""入学日期设置器,支持日期对象或字符串"""
if value is None:
self._enrollment_date = None
elif isinstance(value, str):
try:
self._enrollment_date = date.fromisoformat(value)
except:
self._enrollment_date = None
else:
self._enrollment_date = value
@property
def errors(self) -> Dict[str, str]:
"""获取所有验证错误信息"""
return self._errors
@property
def is_valid(self) -> bool:
"""判断学生信息是否有效"""
return not bool(self._errors)
def validate(self) -> None:
"""验证学生信息的合法性"""
self._errors = {}
# 验证姓名
if not self.name or len(self.name) < 2 or len(self.name) > 20:
self._errors["name"] = "姓名必须在2到20个字符之间"
elif not self.name.isalpha():
self._errors["name"] = "姓名不能包含数字和特殊符号"
# 验证身份证号
if not self.__validate_id_card(self.id_card):
self._errors["id_card"] = "身份证号格式不正确"
# 验证学号
if not self.stu_id:
self._errors["stu_id"] = "学号不能为空"
# 验证身高
if self.height is not None and (self.height < 50 or self.height > 250):
self._errors["height"] = "身高必须在50到250厘米之间"
# 验证体重
if self.weight is not None and (self.weight < 5 or self.weight > 300):
self._errors["weight"] = "体重必须在5到300千克之间"
# 验证入学日期
if self.enrollment_date and self.birthday and self.enrollment_date < self.birthday:
self._errors["enrollment_date"] = "入学日期不能早于出生日期"
@staticmethod
def __validate_id_card(id_card: str) -> bool:
"""验证身份证号是否符合国家标准"""
if not id_card or len(id_card) != 18:
return False
# 检查前17位是否为数字
if not id_card[:17].isdigit():
return False
# 验证出生日期
birth_str = id_card[6:14]
try:
date.fromisoformat(birth_str)
except:
return False
# 验证校验位
factors = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2] # 加权因子
check_codes = '10X98765432' # 校验码映射
total = sum(int(id_card[i]) * factors[i] for i in range(17))
check_code = check_codes[total % 11]
return check_code == id_card[17].upper()
def to_dict(self) -> Dict[str, Any]:
"""将学生对象转换为字典"""
result = self.__dict__.copy()
# 处理日期类型
if result.get('_enrollment_date'):
result['enrollment_date'] = result['_enrollment_date'].isoformat()
# 移除内部属性
if '_birthday' in result:
del result['_birthday']
if '_age' in result:
del result['_age']
if '_errors' in result:
del result['_errors']
return result
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> 'Student':
"""从字典创建学生对象"""
if not isinstance(data, dict):
raise TypeError("数据必须是字典类型")
# 处理入学日期
enrollment_date = data.get('enrollment_date')
if enrollment_date and isinstance(enrollment_date, str):
try:
data['enrollment_date'] = date.fromisoformat(enrollment_date)
except:
data['enrollment_date'] = None
return cls(
name=data.get('name', ''),
id_card=data.get('id_card', ''),
stu_id=data.get('stu_id', ''),
gender=data.get('gender'),
height=data.get('height'),
weight=data.get('weight'),
enrollment_date=data.get('enrollment_date'),
class_name=data.get('class_name'),
major=data.get('major')
)
def __repr__(self) -> str:
"""学生对象的字符串表示"""
attrs = [f"{k}='{v}'" for k, v in self.to_dict().items() if v is not None]
return f"Student({', '.join(attrs)})"
# ============== 数据访问层 ==============
class IStudentDAL(ABC):
"""学生信息数据访问层接口"""
@abstractmethod
def get_by_id(self, id_card: str) -> Optional[Student]:
"""根据身份证号获取学生信息"""
pass
@abstractmethod
def get_by_stu_id(self, stu_id: str) -> Optional[Student]:
"""根据学号获取学生信息"""
pass
@abstractmethod
def get_all(self) -> List[Student]:
"""获取所有学生信息"""
pass
@abstractmethod
def add(self, student: Student) -> bool:
"""添加学生信息"""
pass
@abstractmethod
def delete(self, id_card: str) -> bool:
"""根据身份证号删除学生信息"""
pass
@abstractmethod
def delete_by_stu_id(self, stu_id: str) -> bool:
"""根据学号删除学生信息"""
pass
@abstractmethod
def update(self, student: Student) -> bool:
"""更新学生信息"""
pass
@abstractmethod
def is_exist(self, id_card: str) -> bool:
"""检查学生是否存在"""
pass
@abstractmethod
def is_exist_stu_id(self, stu_id: str) -> bool:
"""检查学号是否存在"""
pass
class JsonStudentDAL(IStudentDAL):
"""JSON文件数据访问实现"""
def __init__(self, file_path: str):
"""初始化JSON数据访问层"""
self.file_path = file_path
self._ensure_file_exists()
def _ensure_file_exists(self) -> None:
"""确保数据文件存在"""
if not os.path.exists(self.file_path):
with open(self.file_path, 'w', encoding='utf-8') as f:
json.dump([], f, ensure_ascii=False, indent=4)
def _load(self) -> List[Dict[str, Any]]:
"""从文件加载学生数据"""
try:
with open(self.file_path, 'r', encoding='utf-8') as f:
return json.load(f)
except (json.JSONDecodeError, FileNotFoundError):
return []
def _save(self, students: List[Dict[str, Any]]) -> None:
"""保存学生数据到文件"""
with open(self.file_path, 'w', encoding='utf-8') as f:
json.dump(students, f, ensure_ascii=False, indent=4)
def get_by_id(self, id_card: str) -> Optional[Student]:
"""根据身份证号获取学生信息"""
students = self._load()
for data in students:
if data.get('id_card') == id_card:
return Student.from_dict(data)
return None
def get_by_stu_id(self, stu_id: str) -> Optional[Student]:
"""根据学号获取学生信息"""
students = self._load()
for data in students:
if data.get('stu_id') == stu_id:
return Student.from_dict(data)
return None
def get_all(self) -> List[Student]:
"""获取所有学生信息"""
students_data = self._load()
return [Student.from_dict(data) for data in students_data]
def add(self, student: Student) -> bool:
"""添加学生信息"""
if self.is_exist(student.id_card) or self.is_exist_stu_id(student.stu_id):
return False
students = self._load()
students.append(student.to_dict())
self._save(students)
return True
def delete(self, id_card: str) -> bool:
"""根据身份证号删除学生信息"""
students = self._load()
original_length = len(students)
students = [s for s in students if s.get('id_card') != id_card]
if len(students) < original_length:
self._save(students)
return True
return False
def delete_by_stu_id(self, stu_id: str) -> bool:
"""根据学号删除学生信息"""
students = self._load()
original_length = len(students)
students = [s for s in students if s.get('stu_id') != stu_id]
if len(students) < original_length:
self._save(students)
return True
return False
def update(self, student: Student) -> bool:
"""更新学生信息"""
students = self._load()
updated = False
for i, data in enumerate(students):
if data.get('id_card') == student.id_card:
students[i] = student.to_dict()
updated = True
break
if updated:
self._save(students)
return updated
def is_exist(self, id_card: str) -> bool:
"""检查学生是否存在"""
return self.get_by_id(id_card) is not None
def is_exist_stu_id(self, stu_id: str) -> bool:
"""检查学号是否存在"""
return self.get_by_stu_id(stu_id) is not None
class CsvStudentDAL(IStudentDAL):
"""CSV文件数据访问实现"""
FIELD_TYPES = {
'height': int,
'weight': float,
'gender': lambda x: True if x == 'True' else False if x == 'False' else None,
}
def __init__(self, file_path: str):
"""初始化CSV数据访问层"""
self.file_path = file_path
self._ensure_file_exists()
def _ensure_file_exists(self) -> None:
"""确保数据文件存在"""
if not os.path.exists(self.file_path):
with open(self.file_path, 'w', encoding='utf-8-sig', newline='') as f:
writer = csv.DictWriter(f, fieldnames=self._get_fieldnames())
writer.writeheader()
def _get_fieldnames(self) -> List[str]:
"""获取CSV文件字段名"""
return [
'name', 'id_card', 'stu_id', 'gender', 'height',
'weight', 'enrollment_date', 'class_name', 'major'
]
def _convert_row(self, row: Dict[str, str]) -> Dict[str, Any]:
"""转换CSV行数据为字典"""
result = {}
for key, value in row.items():
if not value:
result[key] = None
else:
converter = self.FIELD_TYPES.get(key)
if converter:
result[key] = converter(value)
elif key == 'enrollment_date':
try:
result[key] = date.fromisoformat(value)
except:
result[key] = value
else:
result[key] = value
return result
def _load(self) -> List[Dict[str, Any]]:
"""从文件加载学生数据"""
try:
with open(self.file_path, 'r', encoding='utf-8-sig', newline='') as f:
reader = csv.DictReader(f)
return [self._convert_row(row) for row in reader]
except FileNotFoundError:
return []
def _save(self, students: List[Dict[str, Any]]) -> None:
"""保存学生数据到文件"""
with open(self.file_path, 'w', encoding='utf-8-sig', newline='') as f:
writer = csv.DictWriter(f, fieldnames=self._get_fieldnames())
writer.writeheader()
for student in students:
# 转换日期为字符串
if student.get('enrollment_date') and isinstance(student['enrollment_date'], date):
student['enrollment_date'] = student['enrollment_date'].isoformat()
writer.writerow(student)
def get_by_id(self, id_card: str) -> Optional[Student]:
"""根据身份证号获取学生信息"""
students = self._load()
for data in students:
if data.get('id_card') == id_card:
return Student.from_dict(data)
return None
def get_by_stu_id(self, stu_id: str) -> Optional[Student]:
"""根据学号获取学生信息"""
students = self._load()
for data in students:
if data.get('stu_id') == stu_id:
return Student.from_dict(data)
return None
def get_all(self) -> List[Student]:
"""获取所有学生信息"""
students_data = self._load()
return [Student.from_dict(data) for data in students_data]
def add(self, student: Student) -> bool:
"""添加学生信息"""
if self.is_exist(student.id_card) or self.is_exist_stu_id(student.stu_id):
return False
students = self._load()
students.append(student.to_dict())
self._save(students)
return True
def delete(self, id_card: str) -> bool:
"""根据身份证号删除学生信息"""
students = self._load()
original_length = len(students)
students = [s for s in students if s.get('id_card') != id_card]
if len(students) < original_length:
self._save(students)
return True
return False
def delete_by_stu_id(self, stu_id: str) -> bool:
"""根据学号删除学生信息"""
students = self._load()
original_length = len(students)
students = [s for s in students if s.get('stu_id') != stu_id]
if len(students) < original_length:
self._save(students)
return True
return False
def update(self, student: Student) -> bool:
"""更新学生信息"""
students = self._load()
updated = False
for i, data in enumerate(students):
if data.get('id_card') == student.id_card:
students[i] = student.to_dict()
updated = True
break
if updated:
self._save(students)
return updated
def is_exist(self, id_card: str) -> bool:
"""检查学生是否存在"""
return self.get_by_id(id_card) is not None
def is_exist_stu_id(self, stu_id: str) -> bool:
"""检查学号是否存在"""
return self.get_by_stu_id(stu_id) is not None
# ============== 业务逻辑层 ==============
class StudentBLL:
"""学生信息业务逻辑层"""
def __init__(self, file_path: str):
"""初始化业务逻辑层"""
self.file_path = file_path
self.dal = self._get_dal()
def _get_dal(self) -> IStudentDAL:
"""获取数据访问层实例"""
if self.file_path.endswith('.json'):
return JsonStudentDAL(self.file_path)
elif self.file_path.endswith('.csv'):
return CsvStudentDAL(self.file_path)
else:
raise ValueError("不支持的文件格式仅支持JSON和CSV")
def add_student(self, student: Student) -> bool:
"""添加学生信息"""
# 校验学生信息
if not student.is_valid:
raise ValueError(f"学生信息无效: {student.errors}")
# 检查唯一性
if self.dal.is_exist(student.id_card):
raise ValueError("该身份证号已存在")
if self.dal.is_exist_stu_id(student.stu_id):
raise ValueError("该学号已存在")
return self.dal.add(student)
def delete_student(self, id_card: str) -> bool:
"""根据身份证号删除学生"""
if not self.dal.is_exist(id_card):
raise ValueError("学生不存在")
return self.dal.delete(id_card)
def delete_student_by_stu_id(self, stu_id: str) -> bool:
"""根据学号删除学生"""
if not self.dal.is_exist_stu_id(stu_id):
raise ValueError("学生不存在")
return self.dal.delete_by_stu_id(stu_id)
def update_student(self, student: Student) -> bool:
"""更新学生信息"""
if not student.is_valid:
raise ValueError(f"学生信息无效: {student.errors}")
if not self.dal.is_exist(student.id_card):
raise ValueError("学生不存在")
return self.dal.update(student)
def update_student_partial(self, id_card: str, **kwargs) -> bool:
"""部分更新学生信息"""
student = self.dal.get_by_id(id_card)
if not student:
raise ValueError("学生不存在")
# 更新提供的字段
for key, value in kwargs.items():
if hasattr(student, key) and key not in ['id_card', 'stu_id']:
setattr(student, key, value)
# 校验更新后的信息
student.validate()
if student.errors:
raise ValueError(f"更新后学生信息无效: {student.errors}")
return self.dal.update(student)
def get_student_by_id(self, id_card: str) -> Optional[Student]:
"""根据身份证号查询学生"""
return self.dal.get_by_id(id_card)
def get_student_by_stu_id(self, stu_id: str) -> Optional[Student]:
"""根据学号查询学生"""
return self.dal.get_by_stu_id(stu_id)
def get_all_students(self) -> List[Student]:
"""获取所有学生"""
return self.dal.get_all()
def search_by_name(self, name: str, fuzzy: bool = True) -> List[Student]:
"""按姓名查询学生"""
all_students = self.get_all_students()
if fuzzy:
return [s for s in all_students if name.lower() in s.name.lower()]
return [s for s in all_students if s.name == name]
def search_by_class(self, class_name: str, fuzzy: bool = True) -> List[Student]:
"""按班级查询学生"""
all_students = self.get_all_students()
if fuzzy:
return [s for s in all_students if class_name.lower() in (s.class_name or '').lower()]
return [s for s in all_students if s.class_name == class_name]
def search_by_major(self, major: str, fuzzy: bool = True) -> List[Student]:
"""按专业查询学生"""
all_students = self.get_all_students()
if fuzzy:
return [s for s in all_students if major.lower() in (s.major or '').lower()]
return [s for s in all_students if s.major == major]
def count_students(self) -> int:
"""统计学生总数"""
return len(self.get_all_students())
def count_by_major(self) -> Dict[str, int]:
"""按专业统计学生人数"""
result = {}
for student in self.get_all_students():
if student.major:
result[student.major] = result.get(student.major, 0) + 1
return result
def average_height(self) -> float:
"""计算平均身高"""
students = [s for s in self.get_all_students() if s.height is not None]
if not students:
return 0
return sum(s.height for s in students) / len(students)
def average_weight(self) -> float:
"""计算平均体重"""
students = [s for s in self.get_all_students() if s.weight is not None]
if not students:
return 0
return sum(s.weight for s in students) / len(students)
def export_to_json(self, file_path: str) -> bool:
"""导出数据到JSON文件"""
students = [s.to_dict() for s in self.get_all_students()]
try:
with open(file_path, 'w', encoding='utf-8') as f:
json.dump(students, f, ensure_ascii=False, indent=4)
return True
except:
return False
def export_to_csv(self, file_path: str) -> bool:
"""导出数据到CSV文件"""
students = self.get_all_students()
if not students:
return False
fieldnames = list(students[0].to_dict().keys())
try:
with open(file_path, 'w', encoding='utf-8-sig', newline='') as f:
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
for student in students:
writer.writerow(student.to_dict())
return True
except:
return False
def import_from_json(self, file_path: str) -> int:
"""从JSON文件导入数据"""
try:
with open(file_path, 'r', encoding='utf-8') as f:
students_data = json.load(f)
count = 0
for data in students_data:
try:
student = Student.from_dict(data)
if self.add_student(student):
count += 1
except:
continue
return count
except:
return 0
def import_from_csv(self, file_path: str) -> int:
"""从CSV文件导入数据"""
try:
with open(file_path, 'r', encoding='utf-8-sig', newline='') as f:
reader = csv.DictReader(f)
students_data = [row for row in reader]
count = 0
for data in students_data:
try:
student = Student.from_dict(data)
if self.add_student(student):
count += 1
except:
continue
return count
except:
return 0
# ============== 表示层 ==============
class StudentUI:
"""学生信息管理系统用户界面"""
def __init__(self, file_path: str):
"""初始化用户界面"""
self.bll = StudentBLL(file_path)
self.running = True
def display_main_menu(self) -> None:
"""显示主菜单"""
print("\n===== 学生信息管理系统 =====")
print("1. 添加学生信息")
print("2. 删除学生信息")
print("3. 更新学生信息")
print("4. 查询学生信息")
print("5. 统计分析")
print("6. 数据导入导出")
print("7. 清空所有学生信息")
print("0. 退出系统")
print("==========================")
def display_query_menu(self) -> None:
"""显示查询菜单"""
print("\n===== 查询学生信息 =====")
print("1. 查询所有学生")
print("2. 按身份证号查询")
print("3. 按学号查询")
print("4. 按姓名查询")
print("5. 按班级查询")
print("6. 按专业查询")
print("0. 返回上一级")
print("======================")
def display_stats_menu(self) -> None:
"""显示统计菜单"""
print("\n===== 统计分析 =====")
print("1. 学生总数")
print("2. 各专业学生人数")
print("3. 平均身高")
print("4. 平均体重")
print("0. 返回上一级")
print("====================")
def display_import_export_menu(self) -> None:
"""显示数据导入导出菜单"""
print("\n===== 数据导入导出 =====")
print("1. 导出到JSON")
print("2. 从JSON导入")
print("3. 导出到CSV")
print("4. 从CSV导入")
print("0. 返回上一级")
print("========================")
def get_input(self, prompt: str) -> str:
"""获取用户输入"""
return input(prompt).strip()
def add_student(self) -> None:
"""添加学生信息"""
print("\n--- 添加学生信息 ---")
name = self.get_input("姓名: ")
id_card = self.get_input("身份证号: ")
stu_id = self.get_input("学号: ")
gender = self.get_input("性别(1-男, 0-女, 直接回车-未知): ")
if gender == '1':
gender = True
elif gender == '0':
gender = False
else:
gender = None
height_str = self.get_input("身高(cm, 直接回车-未知): ")
height = int(height_str) if height_str else None
weight_str = self.get_input("体重(kg, 保留一位小数, 直接回车-未知): ")
weight = float(weight_str) if weight_str else None
enrollment_date = self.get_input("入学日期(YYYY-MM-DD, 直接回车-未知): ")
enrollment_date = date.fromisoformat(enrollment_date) if enrollment_date else None
class_name = self.get_input("班级名称(直接回车-未知): ")
major = self.get_input("专业(直接回车-未知): ")
try:
student = Student(
name=name,
id_card=id_card,
stu_id=stu_id,
gender=gender,
height=height,
weight=weight,
enrollment_date=enrollment_date,
class_name=class_name,
major=major
)
if self.bll.add_student(student):
print("学生添加成功!")
else:
print("学生添加失败,身份证号或学号已存在!")
except ValueError as e:
print(f"操作失败: {e}")
def delete_student(self) -> None:
"""删除学生信息"""
print("\n--- 删除学生信息 ---")
print("1. 按身份证号删除")
print("2. 按学号删除")
choice = self.get_input("请选择: ")
try:
if choice == '1':
id_card = self.get_input("请输入身份证号: ")
if self.bll.delete_student(id_card):
print("学生删除成功!")
else:
print("学生不存在!")
elif choice == '2':
stu_id = self.get_input("请输入学号: ")
if self.bll.delete_student_by_stu_id(stu_id):
print("学生删除成功!")
else:
print("学生不存在!")
else:
print("无效选择!")
except ValueError as e:
print(f"操作失败: {e}")
def update_student(self) -> None:
"""更新学生信息"""
print("\n--- 更新学生信息 ---")
id_card = self.get_input("请输入要更新的学生身份证号: ")
student = self.bll.get_student_by_id(id_card)
if not student:
print("学生不存在!")
return
print(f"\n当前学生信息: {student}")
name = self.get_input(f"姓名[{student.name}]: ") or student.name
gender = self.get_input(f"性别(1-男, 0-女, 直接回车[{student.gender}]): ")
if gender:
gender = True if gender == '1' else False
else:
gender = student.gender
height_str = self.get_input(f"身高(cm, 直接回车[{student.height}]): ")
height = int(height_str) if height_str else student.height
weight_str = self.get_input(f"体重(kg, 直接回车[{student.weight}]): ")
weight = float(weight_str) if weight_str else student.weight
enrollment_date = self.get_input(f"入学日期(YYYY-MM-DD, 直接回车[{student.enrollment_date}]): ")
enrollment_date = date.fromisoformat(enrollment_date) if enrollment_date else student.enrollment_date
class_name = self.get_input(f"班级名称[{student.class_name}]: ") or student.class_name
major = self.get_input(f"专业[{student.major}]: ") or student.major
try:
updated_student = Student(
name=name,
id_card=id_card,
stu_id=student.stu_id,
gender=gender,
height=height,
weight=weight,
enrollment_date=enrollment_date,
class_name=class_name,
major=major
)
if self.bll.update_student(updated_student):
print("学生信息更新成功!")
else:
print("学生信息更新失败!")
except ValueError as e:
print(f"操作失败: {e}")
def query_student(self) -> None:
"""查询学生信息"""
while True:
self.display_query_menu()
choice = self.get_input("请选择操作: ")
if choice == '0':
break
try:
if choice == '1':
# 查询所有学生
students = self.bll.get_all_students()
if not students:
print("没有学生信息!")
else:
print("\n--- 所有学生信息 ---")
for i, student in enumerate(students, 1):
print(f"{i}. {student}")
elif choice == '2':
# 按身份证号查询
id_card = self.get_input("请输入身份证号: ")
student = self.bll.get_student_by_id(id_card)
if student:
print(f"\n--- 学生信息 ---: {student}")
else:
print("未找到该学生!")
elif choice == '3':
# 按学号查询
stu_id = self.get_input("请输入学号: ")
student = self.bll.get_student_by_stu_id(stu_id)
if student:
print(f"\n--- 学生信息 ---: {student}")
else:
print("未找到该学生!")
elif choice == '4':
# 按姓名查询
name = self.get_input("请输入姓名(支持模糊查询): ")
students = self.bll.search_by_name(name)
if not students:
print("未找到该学生!")
else:
print(f"\n--- 查找结果({len(students)}人) ---")
for i, student in enumerate(students, 1):
print(f"{i}. {student}")
elif choice == '5':
# 按班级查询
class_name = self.get_input("请输入班级名称(支持模糊查询): ")
students = self.bll.search_by_class(class_name)
if not students:
print("未找到该班级的学生!")
else:
print(f"\n--- 查找结果({len(students)}人) ---")
for i, student in enumerate(students, 1):
print(f"{i}. {student}")
elif choice == '6':
# 按专业查询
major = self.get_input("请输入专业名称(支持模糊查询): ")
students = self.bll.search_by_major(major)
if not students:
print("未找到该专业的学生!")
else:
print(f"\n--- 查找结果({len(students)}人) ---")
for i, student in enumerate(students, 1):
print(f"{i}. {student}")
else:
print("无效选择!")
except Exception as e:
print(f"查询出错: {e}")
def show_stats(self) -> None:
"""显示统计信息"""
while True:
self.display_stats_menu()
choice = self.get_input("请选择操作: ")
if choice == '0':
break
try:
if choice == '1':
# 学生总数
count = self.bll.count_students()
print(f"\n学生总数: {count}")
elif choice == '2':
# 各专业学生人数
major_counts = self.bll.count_by_major()
if not major_counts:
print("没有学生信息!")
else:
print("\n各专业学生人数:")
for major, count in major_counts.items():
print(f"{major}: {count}")
elif choice == '3':
# 平均身高
avg_height = self.bll.average_height()
print(f"\n平均身高: {avg_height:.2f}cm")
elif choice == '4':
# 平均体重
avg_weight = self.bll.average_weight()
print(f"\n平均体重: {avg_weight:.2f}kg")
else:
print("无效选择!")
except Exception as e:
print(f"统计出错: {e}")
def import_export_data(self) -> None:
"""数据导入导出"""
while True:
self.display_import_export_menu()
choice = self.get_input("请选择操作: ")
if choice == '0':
break
try:
if choice == '1':
# 导出到JSON
file_path = self.get_input("请输入导出文件路径: ")
if self.bll.export_to_json(file_path):
print(f"数据已成功导出到 {file_path}")
else:
print("导出失败!")
elif choice == '2':
# 从JSON导入
file_path = self.get_input("请输入导入文件路径: ")
count = self.bll.import_from_json(file_path)
print(f"成功导入 {count} 条学生记录")
elif choice == '3':
# 导出到CSV
file_path = self.get_input("请输入导出文件路径: ")
if self.bll.export_to_csv(file_path):
print(f"数据已成功导出到 {file_path}")
else:
print("导出失败!")
elif choice == '4':
# 从CSV导入
file_path = self.get_input("请输入导入文件路径: ")
count = self.bll.import_from_csv(file_path)
print(f"成功导入 {count} 条学生记录")
else:
print("无效选择!")
except Exception as e:
print(f"操作出错: {e}")
def clear_students(self) -> None:
"""清空所有学生信息"""
print("\n--- 清空所有学生信息 ---")
confirm = self.get_input("确定要清空所有学生信息吗?(y/n): ")
if confirm.lower() == 'y':
# 这里简单实现,实际应更安全
open(self.bll.file_path, 'w', encoding='utf-8').close()
print("所有学生信息已清空!")
else:
print("已取消清空操作。")
def run(self) -> None:
"""运行系统"""
print("学生信息管理系统启动中...")
while self.running:
self.display_main_menu()
choice = self.get_input("请选择操作: ")
try:
if choice == '1':
self.add_student()
elif choice == '2':
self.delete_student()
elif choice == '3':
self.update_student()
elif choice == '4':
self.query_student()
elif choice == '5':
self.show_stats()
elif choice == '6':
self.import_export_data()
elif choice == '7':
self.clear_students()
elif choice == '0':
self.running = False
print("感谢使用学生信息管理系统,再见!")
else:
print("无效选择,请重新输入!")
except Exception as e:
print(f"操作出错: {e}")
# ============== 主程序 ==============
if __name__ == "__main__":
# 默认为JSON文件存储
default_file = "students.json"
print(f"使用默认数据文件: {default_file}")
ui = StudentUI(default_file)
ui.run()