C-实现简单成绩管理系统

5次阅读

共计 12189 个字符,预计需要花费 31 分钟才能阅读完成。

前言

这周跟 C#打了一周的交道(本周是学校安排的实验周,然后用到了 C# 实现想要的程序和功能)

一共七个实验,选择三个,我就选择我进步最大的一个来分析一下吧。

效果

先来看一下效果吧


从 txt 文本中读取数据后展示出来



点击目标后选中,然后点击“修改”,弹出修改界面,然后进行编辑即可



点击“统计”按钮,弹出窗口显示各分数段的信息



点击“查询”后,弹出界面,输入后,点击“确定”即可显示信息


实现

一、准备工作

在写方法之前,首先就是先把界面规划好,就是通过添加按钮和输入框或显示框组成一个符合要求的窗口或多个窗口

在这个程序中我们用到的主要是这几个组件

对文件进行操作要进行引用的声明,即“using”

我们添加的是这两行

然后我们还要写一些代码来实现其他功能

 public Form1()
        {InitializeComponent();
            this.listView1.Columns.Add("学号", 100, HorizontalAlignment.Center);
            this.listView1.Columns.Add("姓名", 100, HorizontalAlignment.Center);
            this.listView1.Columns.Add("数学", 100, HorizontalAlignment.Center);
            this.listView1.Columns.Add("英语", 100, HorizontalAlignment.Center);
            this.listView1.Columns.Add("政治", 100, HorizontalAlignment.Center);
            this.listView1.Columns.Add("总分", 100, HorizontalAlignment.Center);
            this.listView1.Columns.Add("平均分", 100, HorizontalAlignment.Center);
            this.listView1.Columns.Add("名次", 100, HorizontalAlignment.Center);
            this.listView1.View = System.Windows.Forms.View.Details;
            this.listView1.FullRowSelect = true;// 是否可以选择行
        }

“listview1”就是按钮上方实现显示的控件,“this”指的就是 Form1 这个窗口,“Columns”指的是“栏”,也就是上方的内容,“add”指的是把后面的内容作为“Columns”的内容,后面的“100”等都是“Columns”的属性,可以通过修改它的属性来修改它的大小和位置,还有一种生成“Column”的方法是通过属性栏来添加。
点击 listview 一次选中它,然后右键单击一次,点击属性,会发现有“Column”这个属性,点进去后就可以进行编辑和修改了。


不得不说确实挺方便的,不过实验报告手册中给了部分必须的源码,再加上自己第一次接触 C#,所以就没使用后面的方法,不过在后面的操作中使用了一下,确实挺爽。

二、读取操作

这里的“读取”按钮读取的是统计之后的内容,并非成绩等信息,双击“读取”按钮后即可进行编辑(在按钮的属性中我修改了 name 属性为 load,所以此处的方法名为“load_Click”)

  private void load_Click(object sender, EventArgs e)
        {this.load_data();
        }

此处调用了“load_data()”这个方法

 public void load_data()
 {string file = File.ReadAllText("Score.txt", UTF8Encoding.Default);
            // 把 txt 文件中按行存储的信息利用 regex.Split 按行存入 string 数组中
            string[] records = Regex.Split(file, "\r\n");
            // 开始更新视图
            this.listView1.BeginUpdate();
            // 清空原有视图
            this.listView1.Items.Clear();
            // records.Length 为数组的元素个数
            for (int index = 0; index < records.Length; index++)
            {   // 分割每行记录的各个字段
                string[] components = Regex.Split(records[index], " ");
                // 生成 listview 的一行
                ListViewItem lvi = new ListViewItem(components);
                // 添加背景色
                lvi.SubItems[0].BackColor = Color.Red;
                // 把新生成的行加入到 listview 中
                this.listView1.Items.Add(lvi);
            }
            // 视图更新结束
            this.listView1.EndUpdate();}

这个方法就是以“/r/n”为分界线定义一个数组 1,然后再以空格为分界线定义一个数组 2,同时生成一个 ListViewItem 来显示数组 2,然后再设置一下背景色,此处设置的为红色

  // 生成 listview 的一行
                ListViewItem lvi =  new ListViewItem(components);
 // 添加背景色
                lvi.SubItems[0].BackColor = Color.Red;

lvi 是新生成的 listview 的命名

三、查询操作
  private void Search_Click(object sender, EventArgs e)
        {Form3 f3 = new Form3();
            f3.Show();}

在查询的方法中我们调用了一个窗口 Form3,同 Form1 一样,先规划好窗口的格局,然后再写方法

private void go_Click(object sender, EventArgs e)
        {string file = File.ReadAllText("Score.txt", UTF8Encoding.Default);
            // 把 txt 文件中按行存储的信息利用 regex.Split 按行存入 string 数组中
            string[] records = Regex.Split(file, "\r\n");
            // 开始更新视图
            this.listView1.BeginUpdate();
            // 清空原有视图
            this.listView1.Items.Clear();
            // records.Length 为数组的元素个数
            for (int index = 0; index < records.Length; index++)
            {

                // 分割每行记录的各个字段
                string[] components = Regex.Split(records[index], " ");

                Regex r = new Regex(this.textBox1.Text); // 定义一个 Regex 对象实例
                Match m = r.Match(components[0]); // 在字符串中匹配

                if (m.Success)
                {
                    // 生成 listview 的一行
                    ListViewItem lvi = new ListViewItem(components);
                    // 添加背景色
                    lvi.SubItems[0].BackColor = Color.White;
                    // 把新生成的行加入到 listview 中
                    this.listView1.Items.Add(lvi);
                }
                else if (components.Length > 1)
                {Match n = r.Match(components[1]);
                    if (n.Success)
                    {
                        // 生成 listview 的一行
                        ListViewItem lvi = new ListViewItem(components);
                        // 添加背景色
                        lvi.SubItems[0].BackColor = Color.White;
                        // 把新生成的行加入到 listview 中
                        this.listView1.Items.Add(lvi);
                    }
                }

            }
            // 视图更新结束
            this.listView1.EndUpdate();}

主要的方法,甚至是唯一的方法,就是上方的“go_Click()”方法,“Score.txt”是存放成绩等信息的,在姓名和学号之中匹配,匹配到了就新建一个 listview 的 item 展示信息

这里的查询用到了匹配的函数 Match(),在此给出官方的链接
Match()

四、删除

删除的思想就是选中要删除的那一行,之后从文件读取所有内容,新建 string 数组存放每一行,一行一行的遍历,直到遇见选中的那一行,从数组中移除,然后把剩余的数组元素写入文本中,WriteAllLines()方法定义中调用了 replace()方法,因此省去了我们删除文件原有内容的操作

 private void Delate_Click(object sender, EventArgs e)// 删除
        {foreach (ListViewItem lvi in listView1.SelectedItems)
            {

                // 把 txt 文件内容读入到 file 中,然后对 string 对象 file 进行相关处理

                string file = File.ReadAllText("Score.txt", UTF8Encoding.Default);

                List<string> lines = new List<string>(Regex.Split(file, "\r\n"));
                lines.RemoveAt(lvi.Index);
                File.WriteAllLines("Score.txt", lines.ToArray(), UTF8Encoding.Default);
            }
            this.load_data();}
五、录入
 private void Input_Click(object sender, EventArgs e)// 录入
        {Form2 f = new Form2(this);
            f.Show();}

这里的录入方法调用了一个新的窗口 Form2,与之前不同的是,这里给 Form2 传了一个参数“this”,即 Form1 这个窗口,因为之后我们要在 Form2 的方法中调用 Form1 的方法。
再来看一下 Form2 的界面和方法

1、接收参数
  private Form1 form;
        public Form2()
        {InitializeComponent();
        }
        public Form2(Form1 form){
            this.form = form;
            InitializeComponent();}

首先进行定义,然后进行赋值,这样的话在之后就可以用定义时使用的名称进行 Form1 的方法的调用了。

2、保存信息

在点击“保存”按钮后,我们要实现文本的写入以及界面的更新,文本的写入我们要写新的方法,界面的更新我们可以调用 Form1 的方法。

  if (this.textBox1.Text != string.Empty && this.textBox2.Text != string.Empty && this.textBox3.Text != string.Empty && this.textBox4.Text != string.Empty && this.textBox5.Text != string.Empty)
            {
                // 利用 string 的加法操作构造新纪录,作为一行,写入 txt 文件中
                string newrecord =  this.textBox1.Text + "" + this.textBox2.Text +" "+ this.textBox3.Text +" "+ this.textBox4.Text +" "+ this.textBox5.Text +"\r\n" ;
                File.AppendAllText("Score.txt", newrecord, UTF8Encoding.Default);
                // 结束 form2 的调用
                this.Dispose(false);
                // 调用 Form1 加载界面的方法
                this.form.load_data();}
            else
            {Form5 f5 = new Form5();
                f5.Show();}

首先判空,如果都不为空,定义新的字符串,赋值,写入文本,为了保证程序的严谨性,加一个 Form5 窗口进行错误的提示

(由于时间的限制,这里也不算很完善,有能力的可以考虑进行各个条件的判断,例如“学号”为空,则弹出的窗口显示“学号不能为空”)
值得注意的是,这里调用的 Form1 中的方法必须是公有的,即“public”。

六、运算
 private void operate_Click(object sender, EventArgs e)
        {
            // 把 txt 文件内容读入到 file 中,然后对 string 对象 file 进行相关处理
            string file = File.ReadAllText("Score.txt", UTF8Encoding.Default);
            // 把 txt 文件中按行存储的信息利用 regex.Split 按行存入 string 数组中
            string[] records = Regex.Split(file, "\r\n");
            // 开始更新视图
            this.listView1.BeginUpdate();
            // 清空原有视图
            this.listView1.Items.Clear();

            System.IO.File.WriteAllText("Score.txt", string.Empty);

            double[] score1 = new double[records.Length];
            double[] score2 = new double[records.Length];

            int num = 0;
            // records.Length 为数组的元素个数
            for (int index = 0; index < records.Length; index++)
            {if (records[index].Length != 0)
                {
                    // 分割每行记录的各个字段
                    string[] components = Regex.Split(records[index], " ");

                    score1[index] = Convert.ToDouble(components[2]) + Convert.ToDouble(components[3]) + Convert.ToDouble(components[4]);
                    score2[index] = (Convert.ToDouble(components[2]) + Convert.ToDouble(components[3]) + Convert.ToDouble(components[4])) / 3.0;
                    num++;
                }
                else
                    break;

            }
            // 冒泡法排序
            int temp;
            for (int i = 0; i < num; i++)
            {
                temp = i;
                for (int j = i + 1; j < num; j++)
                {if (score1[j] > score1[temp])
                        temp = j;
                }
                double t = score1[temp];
                score1[temp] = score1[i];
                score1[i] = t;
            }
            for (int index = 0; index < records.Length; index++)
            {if (records[index].Length != 0)
                {
                    // 分割每行记录的各个字段
                    string[] components = Regex.Split(records[index], " ");

                    for (int i = 1; i <= num; i++)
                    {if (score1[i - 1] == Convert.ToDouble(components[2]) + Convert.ToDouble(components[3]) + Convert.ToDouble(components[4]))
                        {
                            // 利用 string 的加法操作构造新纪录,作为一行,写入 txt 文件中
                            string newrecord = components[0] + "" + components[1] +" "+ components[2] +" "+ components[3] +" "+ components[4] +" "+ score1[i - 1] +" "+ score2[index] +" "+ i +"\r\n";
                            File.AppendAllText("Score.txt", newrecord, UTF8Encoding.Default);
                        }
                    }


                }
                else
                    break;

            }
            // 视图更新结束
            this.listView1.EndUpdate();
            // 刷新界面
            this.load_data();}

题目的要求是计算各学生总分、平均分以及排名,思想就是,先读取文件,存储数据,清空文件,定义总成绩和平均成绩两个数组,分别计算每个人的总成绩与平均成绩,然后用冒泡法对总成绩进行排序,得到排名,然后构造新数组拼接这些内容,再把数组写入文本,更新视图即可。

七、修改

修改的思想很简单,虽说实现了想要的功能,但是也有缺陷,也没想到特别好的解决办法,先来说一下思路吧,首先选中,然后点击“修改”,弹窗,在这里尤其注意得是,弹窗里对应位置要显示选中的内容

在这个图中,我选中的是第二行,那么我就要把它对应的数据显示上去,在实验过程中很多人所谓的修改就是输入新的数据,里面所有的内容都得再写一遍,这样的系统,自己用着指定难受。
(说到这我想插上一件事,吐槽一下最近使用的二课系统吧,是一个微信小程序,这是学校某个团队开发的,总体上还是可以的,最大的不足就是每次打开都得登录,也就是说每次使用都得重新输入账号和密码,用户的体验真的挺差的,但是也不能因为它有不足就否认它,总体上也算挺方便的,值得去学习)
因为以前参与过项目的开发,而且项目的受众就是我们自己,也知道什么样的效果更能使用户满意,所以对修改这个功能也算是有所了解,在实验中,绝大多数人的“修改”功能都没能达到满意的效果,所以说经验积累绝对不是毫无用处的。
继续回到我们的功能实现中来,点击“确定”后,写入文本,那么我们肯定要把之前的数据删掉,在传参后立即删除数据,当然这也就产生了瑕疵,用户不修改数据,那怎么办,先来看代码吧,看完就知道了。

 private void modify_Click(object sender, EventArgs e)// 修改
        {if (listView1.SelectedItems.Count != 0)
            {ListViewItem all = this.listView1.SelectedItems[0];
                Form6 f6 = new Form6(all,this);
                f6.Show();
                // 把数据传输过去之后立即删除数据
                foreach (ListViewItem lvi in listView1.SelectedItems)
                {

                    // 把 txt 文件内容读入到 file 中,然后对 string 对象 file 进行相关处理

                    string file = File.ReadAllText("Score.txt", UTF8Encoding.Default);

                    List<string> lines = new List<string>(Regex.Split(file, "\r\n"));
                    lines.RemoveAt(lvi.Index);
                    File.WriteAllLines("Score.txt", lines.ToArray(), UTF8Encoding.Default);
                }
            }
            else
            {Form4 f4 = new Form4();
                f4.Show();}
        }

如果选中的内容不为空,那就把它的数据存到 ListViewItem 类型的数组中,然后传给 Form6(修改数据窗口的类),同时再把 Form1 这个类传过去,以便调用它的方法,然后使用一个删除方法,如果内容为空,弹出 Form4,提示错误。
同样的,在 Form6 这边先定义,然后接收参数,赋值

    private void Form6_Load(object sender, EventArgs e)
        {this.textBox1.Text = this.content.SubItems[0].Text;
            this.textBox2.Text = this.content.SubItems[1].Text;
            this.textBox3.Text = this.content.SubItems[2].Text;
            this.textBox4.Text = this.content.SubItems[3].Text;
            this.textBox5.Text = this.content.SubItems[4].Text;

        }

在加载方法中为 textBox 赋值,以此来显示数据,修改后点击“确定”,确定的方法和“录入”功能的“确定”的方法相同,说白了就是写入,如果“取消”怎么办,数据已经删除了,既然 textBox 里本来就有数据,那么就把那些数据再写入文本,这样虽然实现了我的功能,但是有瑕疵,就是无论修改数据与否,重新加载后数据都会显示在最后一行。
“确定”方法

  private void Determine_Click(object sender, EventArgs e)
        {if (this.textBox1.Text != string.Empty && this.textBox2.Text != string.Empty && this.textBox3.Text != string.Empty && this.textBox4.Text != string.Empty && this.textBox5.Text != string.Empty)
            {
                // 利用 string 的加法操作构造新纪录,作为一行,写入 txt 文件中
                string newrecord = this.textBox1.Text + "" + this.textBox2.Text +" "+ this.textBox3.Text +" "+ this.textBox4.Text +" "+ this.textBox5.Text +"\r\n";
                File.AppendAllText("Score.txt", newrecord, UTF8Encoding.Default);
                var lines = File.ReadAllLines("Score.txt").Where(arg => !string.IsNullOrWhiteSpace(arg));
                File.WriteAllLines("Score.txt", lines);
                // 结束 form6 的调用
                this.Dispose(false);
            }
            this.form.load_data();}
八、统计

所谓的统计就是计算各科各分数段人数以及各科平均分

  private void Sta_Click(object sender, EventArgs e)// 统计
        {this.Output();
            Form7 f7 = new Form7();
            f7.Show();}

调用一个 output()方法,用 Form7 显示数据

 public string Output()// 写入数据
        {
             string add = string.Empty;

             add += "统计情况:" + "\r\n" + "分数段" + "," + "数学" + "," + "英语" + "," + "政治" + "\r\n";
             add += "<60" + "," + GetGradeSection(1, 0, 59).ToString() + "," + GetGradeSection(2, 0, 59).ToString() + "," + GetGradeSection(3, 0, 59).ToString() + "\r\n";
             add += "60~69" + "," + GetGradeSection(1, 60, 69).ToString() + "," + GetGradeSection(2, 60, 69).ToString() + "," + GetGradeSection(3, 60, 69).ToString() + "\r\n";
             add += "70~79" +  ","  + GetGradeSection(1, 70, 79).ToString() + "," + GetGradeSection(2, 70, 79).ToString() + "," + GetGradeSection(3, 70, 79).ToString() + "\r\n";
             add += "80~89" + "," + GetGradeSection(1, 80, 89).ToString() + "," + GetGradeSection(2, 80, 89).ToString() + "," + GetGradeSection(3, 80, 89).ToString() + "\r\n";
             add += "90~100" + "," + GetGradeSection(1, 90, 100).ToString() + "," + GetGradeSection(2, 90, 100).ToString() + "," + GetGradeSection(3, 90, 100).ToString() + "\r\n";
             add += "平均分" + "," + GetAve(1).ToString() + "," + GetAve(2).ToString() + "," + GetAve(3).ToString() + "\r\n";
                System.IO.File.WriteAllText("List.txt", string.Empty);
                File.AppendAllText("List.txt", add, UTF8Encoding.Default); 
           
            return add;
        }

Output 方法调用了 GetGradeSection()和 GetAve() 两个方法,先生成并定义字符串数组,然后把两个方法返回的数据与原有的信息拼接到数组中,再写入即可。

 public double GetGradeSection(int m ,int low, int high)// 分数段
        {
            int count = 0;
            string file = File.ReadAllText("Score.txt", UTF8Encoding.Default);
            // 把 txt 文件中按行存储的信息利用 regex.Split 按行存入 string 数组中
            string[] records = Regex.Split(file, "\r\n");
            // 开始更新视图
            this.listView1.BeginUpdate();
            // 清空原有视图
            this.listView1.Items.Clear();
            // records.Length 为数组的元素个数
            for (int index = 0; index < records.Length; index++)
            {   // 分割每行记录的各个字段
                if (records[index].Length != 0)
                {string[] components = Regex.Split(records[index], " ");

                    if (m == 1)// 数学
                    {if (Convert.ToDouble(components[2]) >= low && Convert.ToDouble(components[2]) <= high)
                        {count++;}
                    }
                    if (m == 2)// 英语
                    {if (Convert.ToDouble(components[3]) >= low && Convert.ToDouble(components[3]) <= high)
                        {count++;}
                    }
                    if (m == 3)// 政治
                    {if (Convert.ToDouble(components[4]) >= low && Convert.ToDouble(components[4]) <= high)
                        {count++;}
                    }
                }
                else break;
            }
              return count;
        }

先循环,然后进行判断,如果在分数段内就把 count 值加一,最后返回到 Output()方法中

  public double GetAve(int m)// 平均分
        {
            double ave = 0;
            string file = File.ReadAllText("Score.txt", UTF8Encoding.Default);
            // 把 txt 文件中按行存储的信息利用 regex.Split 按行存入 string 数组中
            string[] records = Regex.Split(file, "\r\n");
             int length = records.Length;
            // 开始更新视图
            this.listView1.BeginUpdate();
            // 清空原有视图
            this.listView1.Items.Clear();
            // records.Length 为数组的元素个数
            for (int index = 0; index < records.Length; index++)
            {if (records[index].Length != 0)
                {
                    // 分割每行记录的各个字段

                    string[] components = Regex.Split(records[index], " ");

                    if (m == 1)
                    {ave += double.Parse(components[2]);
                    }
                    if (m == 2)
                    {ave += double.Parse(components[3]);
                    }
                    if (m == 3)
                    {ave += double.Parse(components[4]);
                    }
                }
                else break;
            }
           return ave/length;
        }

把各科对应的所有的分数加在一起,然后除以总人数,即为平均分,返回到 Output()方法中
拼接好的字符串数组写入 List.txt 文本中,在 Form7 中读取,在 Listview 中显示即可

九、清除

清除就是清空所有的文本数据,然后就把空字符写入覆盖即可

    private void ClearAll_Click(object sender, EventArgs e)
        {System.IO.File.WriteAllText("Score.txt", string.Empty);
            System.IO.File.WriteAllText("List.txt", string.Empty);
            this.load_data();}

总结

由于之前没接触过 C#,所以也不知道怎么搞,总结下来就是这一周的收获并不是很大,毕竟整天忙得不可开交,吃饭、睡觉、敲代码,最后一总结发现并没有学到什么真切的知识,毕竟一点都没学,上来就用,这种方式真的难以理解,在此我想说的是,只要敢钻研,肯钻研,再难的问题也有解决的办法,最后补充一句,忙点真的挺好的,每天都很充实,加油,奋斗在路上的人们!!!

正文完
 0