当前位置: 首页 > news >正文

第12篇 综合实战——制作一个学生管理系统 仓颉原生中文编程

第12篇 综合实战——制作一个学生管理系统

**作者:**中文编程倡导者—— 李金雨
联系方式:wbtm2718@qq.com
**目标读者:**编程入门(零基础)
核心理念:使用华为仓颉原生中文编程,体验真正的国产编程语言


一、项目介绍

经过前面11篇的学习,我们已经掌握了仓颉编程语言的基础知识。现在,让我们综合运用这些知识,完成一个完整的项目——学生管理系统

项目功能

  1. 添加学生:输入学生信息并保存
  2. 删除学生:按学号删除学生
  3. 查找学生:按学号或姓名查找
  4. 修改信息:修改学生的成绩等信息
  5. 显示所有:列出所有学生信息
  6. 统计功能:计算平均分、最高分、最低分
  7. 数据持久化:保存到文件,下次启动不丢失

二、项目结构

学生管理系统/ ├── 主程序.cj // 程序入口 ├── 学生类.cj // 学生类定义 ├── 学生管理类.cj // 管理学生的增删改查 ├── 文件操作.cj // 文件读写功能 ├── 界面显示.cj // 菜单和界面 └── 学生数据.json // 数据文件

三、代码实现

1. 学生类(学生类.cj)

// 学生类 - 定义学生的属性和方法 public class 学生 { // 属性 public var 学号: String public var 姓名: String public var 年龄: Int64 public var 语文: Float64 public var 数学: Float64 public var 英语: Float64 // 构造方法 public init(学号: String, 姓名: String, 年龄: Int64, 语文: Float64, 数学: Float64, 英语: Float64) { this.学号 = 学号 this.姓名 = 姓名 this.年龄 = 年龄 this.语文 = 语文 this.数学 = 数学 this.英语 = 英语 } // 计算总分 public func 总分(): Float64 { return 语文 + 数学 + 英语 } // 计算平均分 public func 平均分(): Float64 { return 总分() / 3.0 } // 转换为字符串显示 public func toString(): String { return "学号:${学号},姓名:${姓名},年龄:${年龄}," + "语文:${语文},数学:${数学},英语:${英语}," + "总分:${总分()},平均分:${平均分()}" } // 转换为JSON格式 public func 转为JSON(): String { return """{ "学号": "${学号}", "姓名": "${姓名}", "年龄": ${年龄}, "语文": ${语文}, "数学": ${数学}, "英语": ${英语} }""" } }

2. 学生管理类(学生管理类.cj)

import std.collection.* // 学生管理类 - 管理所有学生 public class 学生管理器 { private var 学生列表: ArrayList<学生> public init() { 学生列表 = ArrayList<学生>() } // 添加学生 public func 添加学生(新学生: 学生): Bool { // 检查学号是否已存在 if (查找学生By学号(新学生.学号) != null) { println("错误:学号 ${新学生.学号} 已存在!") return false } 学生列表.add(新学生) println("学生 ${新学生.姓名} 添加成功!") return true } // 删除学生 public func 删除学生(学号: String): Bool { for (i in 0..学生列表.size) { if (学生列表[i].学号 == 学号) { var 被删除学生 = 学生列表.removeAt(i) println("学生 ${被删除学生.姓名} 已删除!") return true } } println("错误:未找到学号为 ${学号} 的学生!") return false } // 按学号查找 public func 查找学生By学号(学号: String): 学生? { for (某学生 in 学生列表) { if (某学生.学号 == 学号) { return 某学生 } } return null } // 按姓名查找(支持模糊查找) public func 查找学生By姓名(姓名: String): ArrayList<学生> { var 结果 = ArrayList<学生>() for (某学生 in 学生列表) { if (某学生.姓名.contains(姓名)) { 结果.add(某学生) } } return 结果 } // 修改学生成绩 public func 修改成绩(学号: String, 语文: Float64, 数学: Float64, 英语: Float64): Bool { var 目标学生 = 查找学生By学号(学号) if (目标学生 == null) { println("错误:未找到该学生!") return false } 目标学生.语文 = 语文 目标学生.数学 = 数学 目标学生.英语 = 英语 println("学生 ${目标学生.姓名} 的成绩已更新!") return true } // 显示所有学生 public func 显示所有学生(): Unit { if (学生列表.isEmpty()) { println("当前没有学生记录!") return } println("\n========== 所有学生信息 ==========") println("序号\t学号\t\t姓名\t年龄\t语文\t数学\t英语\t总分\t平均分") println("----------------------------------------------------------------") for (i in 0..学生列表.size) { var 某学生 = 学生列表[i] println("${i + 1}\t${某学生.学号}\t${某学生.姓名}\t${某学生.年龄}\t" + "${某学生.语文}\t${某学生.数学}\t${某学生.英语}\t" + "${某学生.总分()}\t${某学生.平均分()}") } println("==================================\n") } // 统计信息 public func 显示统计信息(): Unit { if (学生列表.isEmpty()) { println("当前没有学生记录!") return } var 语文总分 = 0.0 var 数学总分 = 0.0 var 英语总分 = 0.0 var 最高总分 = 0.0 var 最低总分 = 300.0 var 第一名: 学生? = null var 最后一名: 学生? = null for (某学生 in 学生列表) { 语文总分 += 某学生.语文 数学总分 += 某学生.数学 英语总分 += 某学生.英语 var 学生总分 = 某学生.总分() if (学生总分 > 最高总分) { 最高总分 = 学生总分 第一名 = 某学生 } if (学生总分 < 最低总分) { 最低总分 = 学生总分 最后一名 = 某学生 } } var 人数 = Float64(学生列表.size) println("\n========== 统计信息 ==========") println("学生总数:${学生列表.size}人") println("语文平均分:${语文总分 / 人数}") println("数学平均分:${数学总分 / 人数}") println("英语平均分:${英语总分 / 人数}") if (第一名 != null) { println("第一名:${第一名.姓名},总分:${最高总分}") } if (最后一名 != null) { println("最后一名:${最后一名.姓名},总分:${最低总分}") } println("==============================\n") } // 获取所有学生(用于保存到文件) public func 获取所有学生(): ArrayList<学生> { return 学生列表 } // 从文件加载后设置学生列表 public func 设置学生列表(新列表: ArrayList<学生>): Unit { 学生列表 = 新列表 } }

3. 文件操作类(文件操作.cj)

import std.fs.* import std.collection.* import std.convert.* // 文件操作类 - 负责数据的持久化 public class 数据管理器 { private let 文件名 = "学生数据.json" // 保存学生数据到文件 public func 保存数据(学生列表: ArrayList<学生>): Bool { try { var 文件 = File.open(文件名, "w") // 构建JSON数组 var JSON内容 = "[\n" for (i in 0..学生列表.size) { JSON内容 += 学生列表[i].转为JSON() if (i < 学生列表.size - 1) { JSON内容 += "," } JSON内容 += "\n" } JSON内容 += "]" 文件.write(JSON内容) 文件.close() println("数据保存成功!") return true } catch (异常: Exception) { println("保存数据失败:${异常.message}") return false } } // 从文件读取学生数据 public func 读取数据(): ArrayList<学生> { var 学生列表 = ArrayList<学生>() try { if (!File.exists(文件名)) { println("数据文件不存在,将创建新文件。") return 学生列表 } var 文件 = File.open(文件名, "r") var 内容 = 文件.readAll() 文件.close() // 简化的JSON解析(实际项目中应使用JSON库) // 这里为了教学演示,使用简化的解析方式 println("数据读取成功!") } catch (异常: Exception) { println("读取数据失败:${异常.message}") } return 学生列表 } }

4. 界面显示(界面显示.cj)

import std.console.* // 界面类 - 负责显示菜单和接收用户输入 public class 界面 { // 显示主菜单 public func 显示主菜单(): Unit { println("\n=================================") println(" 学生管理系统 v1.0") println("=================================") println("1. 添加学生") println("2. 删除学生") println("3. 查找学生") println("4. 修改成绩") println("5. 显示所有学生") println("6. 统计信息") println("7. 保存数据") println("8. 加载数据") println("0. 退出系统") println("=================================") print("请选择操作(0-8):") } // 显示查找子菜单 public func 显示查找菜单(): Unit { println("\n----- 查找方式 -----") println("1. 按学号查找") println("2. 按姓名查找") println("0. 返回") print("请选择:") } // 获取用户输入的字符串 public func 输入字符串(提示: String): String { print(提示) return Console.readLine() } // 获取用户输入的整数 public func 输入整数(提示: String): Int64 { print(提示) try { return Int64.parse(Console.readLine()) } catch (异常: Exception) { println("输入无效,使用默认值0") return 0 } } // 获取用户输入的浮点数 public func 输入浮点数(提示: String): Float64 { print(提示) try { return Float64.parse(Console.readLine()) } catch (异常: Exception) { println("输入无效,使用默认值0") return 0.0 } } // 显示分隔线 public func 分隔线(): Unit { println("---------------------------------") } // 暂停等待用户按键 public func 暂停(): Unit { println("\n按回车键继续...") Console.readLine() } }

5. 主程序(主程序.cj)

import std.console.* // 主程序 - 程序入口 main() { var 管理器 = 学生管理器() var 数据管理 = 数据管理器() var 用户界面 = 界面() var 运行中 = true println("欢迎使用学生管理系统!") while (运行中) { 用户界面.显示主菜单() var 选择 = 用户界面.输入字符串("") match (选择) { case "1" => { // 添加学生 println("\n----- 添加学生 -----") var 学号 = 用户界面.输入字符串("请输入学号:") var 姓名 = 用户界面.输入字符串("请输入姓名:") var 年龄 = 用户界面.输入整数("请输入年龄:") var 语文 = 用户界面.输入浮点数("请输入语文成绩:") var 数学 = 用户界面.输入浮点数("请输入数学成绩:") var 英语 = 用户界面.输入浮点数("请输入英语成绩:") var 新学生 = 学生(学号, 姓名, 年龄, 语文, 数学, 英语) 管理器.添加学生(新学生) 用户界面.暂停() } case "2" => { // 删除学生 println("\n----- 删除学生 -----") var 学号 = 用户界面.输入字符串("请输入要删除的学号:") 管理器.删除学生(学号) 用户界面.暂停() } case "3" => { // 查找学生 用户界面.显示查找菜单() var 查找方式 = 用户界面.输入字符串("") match (查找方式) { case "1" => { var 学号 = 用户界面.输入字符串("请输入学号:") var 结果 = 管理器.查找学生By学号(学号) if (结果 != null) { println("找到学生:${结果.toString()}") } else { println("未找到该学生!") } } case "2" => { var 姓名 = 用户界面.输入字符串("请输入姓名(支持模糊查找):") var 结果列表 = 管理器.查找学生By姓名(姓名) if (结果列表.isEmpty()) { println("未找到匹配的学生!") } else { println("找到 ${结果列表.size} 个匹配结果:") for (某学生 in 结果列表) { println(某学生.toString()) } } } case _ => {} } 用户界面.暂停() } case "4" => { // 修改成绩 println("\n----- 修改成绩 -----") var 学号 = 用户界面.输入字符串("请输入学号:") var 语文 = 用户界面.输入浮点数("请输入新语文成绩:") var 数学 = 用户界面.输入浮点数("请输入新数学成绩:") var 英语 = 用户界面.输入浮点数("请输入新英语成绩:") 管理器.修改成绩(学号, 语文, 数学, 英语) 用户界面.暂停() } case "5" => { // 显示所有学生 管理器.显示所有学生() 用户界面.暂停() } case "6" => { // 统计信息 管理器.显示统计信息() 用户界面.暂停() } case "7" => { // 保存数据 数据管理.保存数据(管理器.获取所有学生()) 用户界面.暂停() } case "8" => { // 加载数据 var 新列表 = 数据管理.读取数据() 管理器.设置学生列表(新列表) 用户界面.暂停() } case "0" => { // 退出 println("确定要退出吗?未保存的数据将丢失。") var 确认 = 用户界面.输入字符串("请输入 y 确认退出:") if (确认 == "y" || 确认 == "Y") { 运行中 = false println("感谢使用,再见!") } } case _ => { println("无效的选择,请重新输入!") 用户界面.暂停() } } } }

四、语法设计讨论:综合项目中的类型声明

同学们,在完成这个综合项目的过程中,我们又一次、也是最后一次,遇到了仓颉的类型后置语法问题。

看看我们项目中的代码:

public class 学生 { public var 学号: String // 类型后置 public var 姓名: String // 类型后置 public var 年龄: Int64 // 类型后置 public var 语文: Float64 // 类型后置 public init(学号: String, 姓名: String, 年龄: Int64, ...) // 参数类型后置 public func 总分(): Float64 { // 返回值类型后置 // ... } } public class 学生管理器 { private var 学生列表: ArrayList<学生> // 泛型类型后置 public func 查找学生By学号(学号: String): 学生? { // 参数和返回值类型都后置 // ... } }

按照中国人的语言习惯:

  • 我们习惯说"字符串类型的学号"、“整数类型的年龄”
  • 我们习惯说"学生列表类型的变量"
  • 我们习惯说"学生类型的返回值"

但仓颉的写法:

  • var 学号: String→ 读作"学号,字符串类型的"(定语后置)
  • func 总分(): Float64→ 读作"总分方法,浮点数类型的返回值"(定语后置)

如果仓颉能改进成C#风格:

// 假设的改进语法 public class 学生 { public String 学号 // "字符串类型的学号" public String 姓名 public Int64 年龄 public Float64 语文 public init(String 学号, String 姓名, Int64 年龄, ...) // 参数类型前置 public Float64 总分() { // "浮点数类型的总分方法" // ... } } public class 学生管理器 { private ArrayList<学生> 学生列表 // "学生列表类型的学生列表" public 学生? 查找学生By学号(String 学号) { // "学生类型的返回值" // ... } }

这样读起来多么自然:“定义一个学生类,它有字符串类型的学号和姓名,整数类型的年龄,浮点数类型的语文成绩…”

华为在设计仓颉语言时,采用了类似Rust的类型后置语法,这就像是在说"学号字符串类型的"而不是"字符串类型的学号"。现代汉语都是定语前置的,我们希望未来的中文编程语言能真正符合中国人的语言习惯!

五、项目扩展思路

完成基础版本后,你可以尝试添加以下功能:

1. 数据验证

  • 学号不能重复
  • 成绩必须在0-100之间
  • 年龄必须在合理范围内

2. 排序功能

  • 按总分排序
  • 按单科成绩排序
  • 按姓名排序

3. 更多统计

  • 各分数段人数统计
  • 及格率、优秀率计算
  • 班级成绩对比

4. 数据导入导出

  • 导出为Excel格式
  • 从Excel导入
  • 生成成绩报告单

5. 用户登录

  • 管理员和普通用户权限区分
  • 密码验证
  • 操作日志记录

六、本课小结

通过这个项目,我们综合运用了前面学到的所有知识:

  1. 变量和数据类型:存储学生信息
  2. 运算符:计算总分和平均分
  3. 条件判断:验证输入、查找学生
  4. 循环:遍历学生列表
  5. 数组和列表:存储多个学生
  6. 方法:封装各种功能
  7. 类和对象:面向对象设计
  8. 继承和多态:可扩展的架构
  9. 异常处理:错误处理
  10. 文件操作:数据持久化

类型语法思考:在整个项目开发过程中,我们频繁地声明类、属性、方法参数、返回值类型,每一次都要面对类型后置的语法。希望未来的中文编程语言能让类型声明更符合中国人的说话习惯,真正做到"说人话"!

七、课程总结

经过12篇的学习,我们已经完成了仓颉编程语言的入门学习。

我们学到了:

  1. 编程的基本概念和仓颉语言的特点
  2. 变量、数据类型和运算符
  3. 条件判断和循环语句
  4. 数组和列表的使用
  5. 方法的定义和调用
  6. 面向对象编程(类、对象、继承、多态)
  7. 异常处理
  8. 文件操作
  9. 综合项目实战

关于语法设计的思考:

在学习过程中,我们反复提到了仓颉语言的类型后置语法问题。虽然仓颉是华为开发的中文编程语言,但在语法设计上采用了类似Rust的类型后置风格,这与中国人日常说话的"定语前置"习惯不太一致。

我们希望未来的中文编程语言能够:

  • 采用类型前置的语法(如C#风格)
  • 更符合中国人的语言习惯
  • 真正做到"用中文说人话"来编程

继续学习的建议:

  1. 多做练习,巩固基础知识
  2. 尝试修改和扩展学生管理系统
  3. 思考生活中可以用编程解决的问题
  4. 学习更多编程语言,对比它们的优缺点
  5. 关注国产编程语言的发展

祝你在编程的道路上越走越远!


http://www.jsqmd.com/news/773299/

相关文章:

  • Apache Airflow 系列教程 | 番外篇:通过 REST API 动态创建 DAG
  • 【四级】2025年12月英语四级真题试卷及答案解析电子版PDF(第一、二、三套全)
  • 对比直接使用官方API体验Taotoken在模型切换与成本控制上的便利
  • Obsidian的博客园同步插件配置
  • 特斯拉Model 3/Y CAN总线DBC文件终极指南:从零到精通的完整实战教程
  • iW610-01C‌ 是瑞萨电子(Renesas Electronics)推出的‌智能同步整流控制器‌,专为高效率 AC/DC 电源转换设计,广泛应用于快充适配器、高功率密度电源等场景。
  • 2024长春相机回收服务商深度**:专业、便捷、高价是核心标准 - 2026年企业推荐榜
  • AssetStudio音频提取实战指南:从Unity资源到MP3/WAV的完整解决方案
  • 五级地址解析是什么?为什么比四级多了行政村
  • 2026年度多路数据采集仪厂家怎么选?老品牌JINKO金科6大主流代表型号详解!附10条DAQ专业FAQ问答! - 奋斗者888
  • 如何快速掌握OR-Tools:5个高效优化算法的终极指南
  • Go语言的并发安全
  • 2026年最新松原路灯采购指南:从厂家实力到场景适配的深度解析 - 2026年企业推荐榜
  • 移动物联赋能的多智能农机联合优化协同作业旅行商问题【附代码】
  • Go语言的容器化和部署
  • VirtualRouter:将Windows电脑变身为智能无线共享中心的十年经典
  • 开源量化期权交易框架FlowAlgo:从事件驱动到希腊字母风控
  • 零基础入门 详解企业主流数据库MySQL8.0
  • 如何用立即执行函数(IIFE)创建独立的作用域隔离变量
  • 从‘光斑’到‘M²因子’:一文读懂激光光束质量参数(附ISO 11146标准解读)
  • ISL95856HRZ-T‌ 是瑞萨电子(Renesas,原Intersil)推出的 ‌4+3多相PWM电压调节器‌,专为Intel IMVP8™桌面CPU设计,提供核心(IA)与核显(GT)双轨供电
  • 2026年5月新发布:安徽梯友电梯配套工程有限公司,青海中式风电梯装潢的匠心之选 - 2026年企业推荐榜
  • SenseNova-U1:原生多模态统一范式的革命性突破
  • 一站式大模型评估框架EvalScope:从原理到实战的完整指南
  • 从订单到收款:手把手带你走通SAP SD标准流程(VA01/VL01N/VF01实战)
  • Go语言的性能优化技巧
  • 明日方舟游戏素材库:一站式解决二次元游戏美术资源需求
  • 3分钟掌握百度网盘秒传技术:永久分享文件的完整指南
  • 5.8
  • 第1篇:认识ArkTS——搭建鸿蒙开发环境