功能清单

  • 添加/删除学生

  • 录入或修改某科成绩、删除某科成绩

  • 查看所有学生(学号、姓名、科目数)

  • 查看某学生成绩单 + 统计(总分/平均/最高/最低)

  • 查看班级某科统计(平均、最高/最低及对应学生)

  • CSV 文件持久化(默认 grades.csv 自动加载/保存)

# git 克隆地址
git clone https://gitcode.com/DeppBing/grade-manager.git

下载地址 

App 

package com.example.grades;

import java.util.*;

public class App {
    private static final Scanner SC = new Scanner(System.in); // 创建一个Scanner对象,用于从控制台读取输入
    private static final GradeBook BOOK = new GradeBook(); // 创建一个GradeBook对象,用于存储学生信息
    private static final CsvStorage STORAGE = new CsvStorage("grades.csv"); // 创建一个CsvStorage对象,用于保存和加载数据

    /*
    * 该文件主要执行一些 初始化操作
    * */
    public static void main(String[] args) {  // 主线程类 用于 启动程序和执行选择操作功能
        // Try auto-load if file exists
        try {
            int loaded = STORAGE.loadInto(BOOK); // 尝试从文件加载
            if (loaded >= 0) { // 如果成功加载
                System.out.println("已自动从 grades.csv 加载记录,共 " + loaded + " 条。");
            }
        } catch (Exception e) {
            System.out.println("自动加载失败(可忽略): " + e.getMessage());
        }

        boolean running = true; // 控制循环的变量
        while (running) {
            printMenu(); // 显示菜单
            String choice = prompt("请选择操作"); // 提示用户输入选项
            switch (choice) { // 根据用户选择执行相应的操作
                case "1" -> addStudent();
                case "2" -> removeStudent();
                case "3" -> addOrUpdateGrade();
                case "4" -> removeGrade();
                case "5" -> listStudents();
                case "6" -> studentReport();
                case "7" -> subjectStats();
                case "8" -> save();
                case "9" -> load();
                case "0" -> { running = false; System.out.println("再见!"); }
                default -> System.out.println("无效选项,请重试。");
            }
        }
    }

    private static void printMenu() {  // 菜单
        System.out.println("\n==== 学生成绩管理系统(控制台版) ====");
        System.out.println("1. 添加学生");
        System.out.println("2. 删除学生");
        System.out.println("3. 录入/修改成绩");
        System.out.println("4. 删除某科成绩");
        System.out.println("5. 查看所有学生");
        System.out.println("6. 查看某学生成绩单与统计");
        System.out.println("7. 查看班级某科统计");
        System.out.println("8. 保存到文件");
        System.out.println("9. 从文件加载");
        System.out.println("0. 退出");
    }

    private static String prompt(String msg) { // 提示用户输入
        System.out.print(msg + ":");
        /*
        * trim() 方法 去除字符串两端的空白字符
        * nextLine() 方法 读取一行输入信息
        * */
        return SC.nextLine().trim(); // 读取用户输入 并 返回
    }

    private static void addStudent() { // 添加学生
        String id = prompt("学号");
        if (id.isEmpty()) { System.out.println("学号不能为空"); return; }
        String name = prompt("姓名");
        if (name.isEmpty()) { System.out.println("姓名不能为空"); return; }
        boolean ok = BOOK.addStudent(new Student(id, name));
        System.out.println(ok ? "添加成功" : "学号已存在,添加失败");
    }

    private static void removeStudent() { // 删除学生
        String id = prompt("要删除的学号");
        boolean ok = BOOK.removeStudent(id);
        System.out.println(ok ? "已删除" : "未找到该学号");
    }

    private static void addOrUpdateGrade() { // 录入/修改成绩
        String id = prompt("学号");
        Student s = BOOK.getStudent(id);
        if (s == null) { System.out.println("未找到该学号"); return; }
        String subject = prompt("科目名");
        String scoreStr = prompt("成绩(0~100,可小数)");
        try {
            double score = Double.parseDouble(scoreStr);
            if (score < 0 || score > 100) throw new IllegalArgumentException("成绩范围应在 0~100");
            s.setGrade(subject, score);
            System.out.println("录入/修改成功");
        } catch (Exception e) {
            System.out.println("成绩格式错误:" + e.getMessage());
        }
    }

    private static void removeGrade() { // 删除某科成绩
        String id = prompt("学号");
        Student s = BOOK.getStudent(id); // 获取学生 getStudent  方法, 返回该学号的学生
        if (s == null) { System.out.println("未找到该学号"); return; }
        String subject = prompt("要删除的科目名");
        boolean ok = s.removeGrade(subject);
        System.out.println(ok ? "该科成绩已删除" : "未找到该科成绩");
    }

    private static void listStudents() { // 查看所有学生
        List<Student> list = BOOK.listStudents();
        if (list.isEmpty()) { System.out.println("暂无学生。"); return; }
        System.out.println("学号\t姓名\t科目数");
        for (Student s : list) {
            System.out.printf("%s\t%s\t%d%n", s.getId(), s.getName(), s.getGrades().size());
        }
    }

    private static void studentReport() { // 查看某学生成绩单与统计
        String id = prompt("学号");
        Student s = BOOK.getStudent(id);
        if (s == null) { System.out.println("未找到该学号"); return; }
        System.out.println("\n--- 成绩单 ---");
        if (s.getGrades().isEmpty()) {
            System.out.println("暂无成绩记录。");
        } else {
            System.out.println("科目\t成绩");
            s.getGrades().forEach((sub, sc) -> System.out.printf("%s\t%.2f%n", sub, sc));
        }
        System.out.println("\n--- 统计 ---");
        System.out.printf("总分:%.2f%n", s.total());
        System.out.printf("平均分:%.2f%n", s.average());
        System.out.printf("最高分:%s%n", s.maxEntry() == null ? "-" : s.maxEntry().getKey() + " " + String.format("%.2f", s.maxEntry().getValue()));
        System.out.printf("最低分:%s%n", s.minEntry() == null ? "-" : s.minEntry().getKey() + " " + String.format("%.2f", s.minEntry().getValue()));
    }

    private static void subjectStats() { // 查看班级某科统计
        String subject = prompt("科目名");
        GradeBook.SubjectStats stats = BOOK.statsForSubject(subject);
        if (stats.count == 0) {
            System.out.println("该科目暂无成绩。");
            return;
        }
        System.out.println("\n--- 班级《" + subject + "》统计 ---");
        System.out.printf("人数:%d%n", stats.count);
        System.out.printf("平均分:%.2f%n", stats.average);
        System.out.printf("最高分:%.2f(%s)%n", stats.maxScore, stats.maxStudentName);
        System.out.printf("最低分:%.2f(%s)%n", stats.minScore, stats.minStudentName);
    }

    private static void save() { // 保存到文件
        try {
            int n = STORAGE.saveFrom(BOOK);
            System.out.println("已保存到 grades.csv,共写入记录:" + n);
        } catch (Exception e) {
            System.out.println("保存失败:" + e.getMessage());
        }
    }

    private static void load() { // 从文件加载
        try {
            BOOK.clear();
            int n = STORAGE.loadInto(BOOK);
            System.out.println("已加载,共读取记录:" + n);
        } catch (Exception e) {
            System.out.println("加载失败:" + e.getMessage());
        }
    }
}

CsvStorage

package com.example.grades;

import java.io.*;  // 导入io全部的包
import java.nio.charset.StandardCharsets; // nio.charset.StandardCharsets 方法用于获取 UTF-8 编码
import java.nio.file.*; // nio.file.Paths 方法用于获取 Path 对象
import java.util.*; // java.util 包含了util下的所有集合框架

/**
 * 简单 CSV 存储:每行格式
 * studentId,studentName,subject,score
 * 当某学生暂无成绩时,subject 与 score 留空一行避免信息丢失。
 */
public class CsvStorage { // 存储器
    private final Path path; // 文件路径  private 修饰符,用于创建一个只读的成员变量,确保成员变量的值不能被修改

    public CsvStorage(String fileName) {
        this.path = Paths.get(fileName);
    } // 有参构造函数

    public int saveFrom(GradeBook book) throws IOException { // 保存
        List<String> lines = new ArrayList<>();
        for (Student s : book.listStudents()) {
            if (s.getGrades().isEmpty()) {
                lines.add(escape(s.getId()) + "," + escape(s.getName()) + ",,");
            } else {
                for (Map.Entry<String, Double> e : s.getGrades().entrySet()) {
                    lines.add(escape(s.getId()) + "," + escape(s.getName()) + "," + escape(e.getKey()) + "," + e.getValue());
                }
            }
        }
        Files.write(path, lines, StandardCharsets.UTF_8, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
        return lines.size();
    }

    public int loadInto(GradeBook book) throws IOException { // 加载
        if (!Files.exists(path)) return 0; // 文件不存在
        List<String> lines = Files.readAllLines(path, StandardCharsets.UTF_8); // 读取文件
        Map<String, Student> temp = new LinkedHashMap<>(); // 临时存储  linkedHashMap 是一个有序的哈希表
        /*
        * for (String line : lines)
        * 等同
        * for (int i = 0; i < lines.size(); i++)
        * */
        for (String line : lines) {
            if (line.isBlank()) continue;
            String[] parts = splitCsv(line);
            String id = parts.length > 0 ? unescape(parts[0]) : "";
            String name = parts.length > 1 ? unescape(parts[1]) : "";
            String subject = parts.length > 2 ? unescape(parts[2]) : "";
            String scoreStr = parts.length > 3 ? parts[3] : "";
            Student s = temp.computeIfAbsent(id, k -> new Student(id, name));
            /*
            * isEmpty() 方法判断字符串是否为空或长度为 0
            * */
            if (subject != null && !subject.isEmpty() && scoreStr != null && !scoreStr.isEmpty()) {
                try {
                    double score = Double.parseDouble(scoreStr);
                    s.setGrade(subject, score);
                } catch (NumberFormatException ignored) {}
            }
        }
        book.clear();
        /*
        * for (Student s : temp.values()) book.addStudent(s);
        * 等同于
        * for ( int i = 0; i < temp.size(); i++) {
        *   book.addStudent(temp.get(i));
        * }
        * */
        for (Student s : temp.values()) book.addStudent(s);
        return lines.size();
    }

    private static String escape(String s) { // escape 字符串
        if (s == null) return "";
        /*
        * contains() 方法用于判断字符串中是否包含指定的字符或字符串。
        * replace() 方法用于将字符串中指定的子串替换为另一个子串。
        * */
        if (s.contains(",") || s.contains("\"") || s.contains("\n")) {
            return "\"" + s.replace("\"", "\"\"") + "\"";
        }
        return s;
    }

    private static String unescape(String s) { //
        s = s.trim();
        if (s.startsWith("\"") && s.endsWith("\"") && s.length() >= 2) {
            s = s.substring(1, s.length()-1).replace("\"\"", "\"");
        }
        return s;
    }

    private static String[] splitCsv(String line) { // 分割 CSV
        List<String> parts = new ArrayList<>(); // 创建一个 ArrayList
        StringBuilder cur = new StringBuilder(); // 创建一个 StringBuilder
        boolean inQuotes = false; // 判断是否在引号中
        for (int i = 0; i < line.length(); i++) {
            char c = line.charAt(i); // charAt() 方法返回指定索引处的 char 值。
            if (c == '\"') { // 判断是否在引号中
                if (inQuotes && i+1 < line.length() && line.charAt(i+1) == '\"') { // 判断是否是转义字符
                    cur.append('\"'); // 添加转义字符
                    i++; // 跳过下一个字符
                } else { // 否则,判断是否是引号
                    inQuotes = !inQuotes; // 切换引号状态
                }
            } else if (c == ',' && !inQuotes) { // 否则,判断是否是逗号
                parts.add(cur.toString()); // 添加当前部分
                cur.setLength(0); // 清空当前部分
            } else { // 否则,添加字符
                cur.append(c); // 添加字符
            }
        }
        parts.add(cur.toString()); // 添加当前部分
        return parts.toArray(new String[0]); // 转换为数组并返回
    }
}

全部代码可点击链接下载,无需会员积分即可下载

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐