学校人员管理系统(go语言纯文件操作)

学校人员管理系统(go语言纯文件操作)

1.概述

本次学习任务是自建一个学校人员管理系统,其中包含三个类六个功能

三个类分别是person、student和teacher类。person类包含编号、姓名和年龄变量,而student和teacher类分别继承person类,且student还包含年级变量,teacher类包含学生数变量。

六个功能分别是:

  1. 增加人员信息:实现添加人员功能,将信息录入一个文件中。
  2. 显示人员信息:显示学校人员信息。
  3. 查找人员信息:根据编号或姓名查找人员。
  4. 修改人员信息:按照编号修改个人信息。
  5. 删除人员信息:按照编号删除人员信息。
  6. 退出管理系统

2.问题分析

三个类的创建没什么好说的,重点在于后面实现的六个功能。(大部分bug异常什么的也都是在六个功能的实现中产生的)

现在我们逐个分析一下:

  1. 主要是文件的操作,重点在文件的读写,录入信息的同时还要防止编号或姓名重复,不然第三个功能会出现问题(一个编号对应多个人…)。
  2. 这没什么好说的,就直接文件读取就行了。
  3. 按照编号查找还行,可以查找到对应信息就直接输出编号下面几行所需要的信息 (大部分人应该都是按顺序读写吧?) 。按照姓名查找就有点麻烦了,需要储存名字对应的上一行编号信息,并且输出,多了一步储存操作 (当然,如果你不是顺序存储当我没说)
  4. 修改有点难受,网上查找并没有找到合适的方法,只能在相应位置开始,进行覆盖,不过覆盖有一些问题,就是覆盖的时候会覆盖到不想覆盖的内容(如果是txt文档的话,其实所有信息都是在同一行的,只是在该换行的地方放了个换行符,从而在显示的时候产生换行效果,所以信息长度过长,会影响下一行信息),所以可以对所有的写入设定一个标准长度,覆盖修改的时候就可以直接按照标椎长度写入。
  5. 并不知道如何删除QAQ 网上求助无果后,就直接按照标椎长度,用空格进行覆盖来实现删除功能 (就是在文档中会产生一些空行,这并不影响实际操作)
  6. 这有手就行吧?

3.解决方法

1.三个类

三个类没什么好说的,直接上代码:

// 定义三个结构体
type person struct {
	id    int
	name  string
	years int
}

type student struct {
	person
	grade int
}

type teacher struct {
	person
	count int
}

2.六个功能

六个功能实现起来就有点多了,一共900+行数,就放比较重要的代码了:

  1. 三个类,就实现三个添加方法,分别添加到三个文档 (我才不会告诉你我是为了美观才这么做的)

    • 就举person类为例,输入、判断是否合理就不放代码了,重点放在id和姓名冲突的判断上,这里的思路是循环所有文件,检索“:”之后的信息(因为我的信息储存都是在“:”之后的),判断是否相同,不同才写入。
    • 代码如下
    •   func (p *person) addPerson() {
             var per person
             filePath := "../person.txt" //打开相应类型的文件
             file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
             if err != nil {
                 fmt.Println("信息写入失败, error=", err)
                 return
             }
             defer file.Close()
             fmt.Println("请分别输入id(9位数),姓名(长度不可超出9{一个汉字长度3其余长度1}),年龄(介于0到150岁之间)")
             fmt.Println("格式如下")
             fmt.Println("(id:)100000000  (姓名:)xxxiix (年龄)100 【期间以一个空格区分】")
             fmt.Scanf("%d %s %d\n", &per.id, &per.name, &per.years) //获取基本信息
             p.SetPerson(per, false, 0)                              //判断并赋值给p person结构体
             if p.id == 0 || p.name == "" {                          //如果id和姓名为默认值,则出现冲突,直接返回
                 fmt.Println("输入失败")
                 return
             }
             sid := "其他人员编号:" + fmt.Sprintf("%d", p.id) + "\n" //修改成相应的标准字符
             sname := "姓名:" + p.name + "\n"
             syears := "年龄:" + fmt.Sprintf("%d", p.years) + "\n"
             for len(sid) < 33 { //保证字符长度均为一致
                 sid += " "
             }
             for len(sname) < 33 {
                 sname += " "
             }
             for len(syears) < 33 {
                 syears += " "
             }
             write := bufio.NewWriter(file) //写入文件
             write.WriteString(sid + "\n")
             write.WriteString(sname + "\n")
             write.WriteString(syears + "\n")
             fmt.Println("添加成功")
             write.Flush() //完成输入
         }
      
         func (p *person) SetPerson(per person, change bool, oid int) {
             for n := 1; n < 4; n++ { //循环所有文件
                 var str string
                 if n == 3 {
                     str = "person"
                 } else if n == 2 {
                     str = "teacher"
                 } else if n == 1 {
                     str = "student"
                 }
                 filePath := "../" + str + ".txt" //寻找相应文件
                 file, err := os.OpenFile(filePath, os.O_RDONLY, 0666)
                 if err != nil {
                     fmt.Println("文件读取失败, error=", err)
                     return
                 }
                 reader := bufio.NewReader(file) //读取文件内容
                 for {
                     s, err := reader.ReadString('\n') //一行一行获取文件信息
                     c := strings.Index(s, ":")
                     st := s[c+1:] //获取文件中:之后的部分,用于对比id和姓名是否冲突
                     if err == io.EOF {
                         break
                     } //下面判断是否冲突,并保证修改模式情况下原id可以和新id相同
                     if st == fmt.Sprintf("%d", per.id)+"\n" && (!change || (change && oid != per.id)) {
                         fmt.Println("id冲突")
                         return
                     } //下面保证在非修改模式下,姓名冲突会导致导入失败,修改模式则不处理姓名冲突
                     if st == per.name+"\n" && !change {
                         fmt.Println("姓名冲突")
                         return
                     }
                 }
                 defer file.Close()
             }
             if per.id < 1e8 || per.id >= 1e9 { //判断id是否正确
                 fmt.Println("输入id有误")
                 return
             } else {
                 p.id = per.id
             }
             if len(per.name) > 9 { //判断姓名是否正确
                 fmt.Println("输入姓名有误")
                 return
             } else {
                 p.name = per.name
             }
             if per.years < 0 || per.years > 150 { //判断年龄是否正确
                 fmt.Println("输入年龄有误")
                 return
             } else {
                 p.years = per.years
             }
         }
      
    • 添加功能就这样了。
  2. 全体人员的信息显示和上面第一个代码类似,就不放了,单个类的信息显示就是没有全体人员没有循环的情况,一样的处理就行了,这里只举person类为例。

    • 代码如下:
    •    func (p *person) displayPerson() {
             filePath := "../person.txt"
             file, err := os.OpenFile(filePath, os.O_RDONLY, 0666)
             if err != nil {
                 fmt.Println("文件读取失败, error=", err)
                 return
             }
             reader := bufio.NewReader(file)
             for {
                 s, err := reader.ReadString('\n')
                 if len(s) > 2 {
                     if s[0] == ' ' {//由于写入的是标准格式,有两个换行符,所有会产生一行信息和一行空行,这里保证空行不输出(美观)
                         s = "   "
                     }
                 }
                 c := strings.TrimLeft(s, " ")
                 if err == io.EOF {
                     break
                 }
                 fmt.Print(c)
                 defer file.Close()
             }
         }
      
    • 显示功能就这样了。
  3. 查找人员信息的话,其实也就是循环所有文件,查找相应的信息,并且输入附近需要的信息(姓名要求输出上两行{一行空行}的信息,所以需要两个变量储存前两行的信息,编号的话就不用这样处理),循环的部分同上,这里就放查找姓名,并输出相应信息的代码。

    • 代码如下:
    •   func searchName(name string) {
            sname := name + "\n"
            for i := 3; i > 0; i-- {
                var str string
                var m int
                if i == 3 {
                    str = "person"
                    m = 0
                } else if i == 2 {
                    str = "teacher"
                    m = 2
                } else if i == 1 {
                    str = "student"
                    m = 2
                } else {
                    return
                }
                filePath := "../" + str + ".txt" //打开对应文件
                file, err := os.OpenFile(filePath, os.O_RDONLY, 0666)
                if err != nil {
                    fmt.Println("文件读取失败, error=", err)
                    return
                }
                reader := bufio.NewReader(file) //读取对应文件
                l := 0
                var sh, ch string
                for {
                    s, err := reader.ReadString('\n') //一行一行读取
                    if len(s) > 2 {                   //并忽略空行
                        if s[0] == ' ' {
                            s = "   "
                        }
                    }
                    if l == 5+m { //姓名相同情况下的结束模式
                        c := strings.TrimLeft(ch, " ")
                        fmt.Print(c)
                        return
                    }
                    c := strings.Index(s, ":")
                    st := s[c+1:]
                    if sname == st {
                        l = 1
                    }
                    if l > 0 && l < 5+m { //姓名相同情况下的输出模式
                        c := strings.TrimLeft(ch, " ")
                        fmt.Print(c)
                        l++
                        ch = sh
                        sh = s
                        continue
                    }
                    if err != nil {
                        break
                    }
                    ch = sh
                    sh = s
                }
            }
        }
      
    • 查找功能就这样了。
  4. 重头戏来了,感觉最困难的一个功能实现就是修改功能。首先要循环读入文件,通过id查找到相应的位置,然后再输入修改内容,保证修改后的id不与其他id冲突且可以是原本的id,之后再变为标准格式对文件相应位置进行覆盖,最后完成写入。(就是因为这里的修改可能会因为长度问题覆盖了不该覆盖的东西,所有我在前面的所有写入的方法里都限定了标准格式)

    • 下面只举person为例 (虽然其他的也就差最后方法不一样了)
    •   func changeModel() {
            var id int
            var per, pers person
            var stu, stud student
            var tea, teac teacher
            var sid, sname, syears string
            fmt.Println("请输入一个编号")
            fmt.Scanf("%d\n", &id)   //获取id
            for n := 3; n > 0; n-- { //循环查找,和之前循环类似
                var str string
                var m, l int //m是获取并修改的额外行数,只对学生和老师有效;l是判断人员类型
                if n == 3 {
                    str = "person"
                    m = 0
                    l = 1
                } else if n == 2 {
                    str = "teacher"
                    m = 2
                    l = 2
                } else if n == 1 {
                    str = "student"
                    m = 2
                    l = 3
                } else {
                    return
                }
                filePath := "../" + str + ".txt" //打开对应文件
                file, err := os.OpenFile(filePath, os.O_RDWR, 0666)
                if err != nil {
                    fmt.Println("文件读取失败, error=", err)
                    return
                }
                defer file.Close()
                lens := 0
                long := 0
                i := 0
                j := 0
                reader := bufio.NewReader(file) //读取文件内容
                var scount, sgrade string
                for {
                    s, err := reader.ReadString('\n') //一行一行读取
                    c := strings.Index(s, ":")
                    st := s[c+1:] //获得:后的有效信息
                    sid = fmt.Sprintf("%d", id) + "\n"
                    if st == sid { //判断该行是否存在id相等
                        i = 1 //储存相等的信息
                        j = 1 //同上,不过只在此次循环存储
                    }
                    long = lens    //获取并保存这一行开始的位置
                    lens += len(s) //进入下一行开始的位置
                    if j == 1 {    //仅获取一次修改后的信息
                        if l == 1 {
                            pers.changePerson(per, id)
                            if pers.id == 0 || pers.name == "" {
                                return
                            }
                            sid = "其他人员编号:" + fmt.Sprintf("%d", pers.id) + "\n" //修改成标准模式,下同
                            for len(sid) < 33 {
                                sid += " "
                            }
                            sname = "姓名:" + pers.name + "\n"
                            for len(sname) < 33 {
                                sname += " "
                            }
                            syears = "年龄:" + fmt.Sprintf("%d", pers.years) + "\n"
                            for len(syears) < 33 {
                                syears += " "
                            }
                        } else if l == 2 {
                            teac.changeTeacher(tea, id)
                            if teac.id == 0 || teac.name == "" || teac.count == 0 {
                                fmt.Println("修改失败")
                                return
                            }
                            sid = "老师编号:" + fmt.Sprintf("%d", teac.id) + "\n" //修改成标准模式,下同
                            for len(sid) < 33 {
                                sid += " "
                            }
                            sname = "姓名:" + teac.name + "\n"
                            for len(sname) < 33 {
                                sname += " "
                            }
                            syears = "年龄:" + fmt.Sprintf("%d", teac.years) + "\n"
                            for len(syears) < 33 {
                                syears += " "
                            }
                            scount = "所教学生数:" + fmt.Sprintf("%d", teac.count) + "\n"
                            for len(scount) < 33 {
                                scount += " "
                            }
                        } else if l == 3 {
                            stud.changeStudent(stu, id)
                            if stud.id == 0 || stud.name == "" || stud.grade == 0 {
                                fmt.Println("修改失败")
                                return
                            }
                            sid = "学生编号:" + fmt.Sprintf("%d", stud.id) + "\n" //修改成标准模式,下同
                            for len(sid) < 33 {
                                sid += " "
                            }
                            sname = "姓名:" + stud.name + "\n"
                            for len(sname) < 33 {
                                sname += " "
                            }
                            syears = "年龄:" + fmt.Sprintf("%d", stud.years) + "\n"
                            for len(syears) < 33 {
                                syears += " "
                            }
                            sgrade = "所教学生数:" + fmt.Sprintf("%d", stud.grade) + "\n"
                            for len(scount) < 33 {
                                scount += " "
                            }
                        }
                        j = 0 //结束获取信息,并保证之后循环不会重复获取
                    }
                    if i > 0 { //进入修改
                        for i <= 6+m { //由于存储时是一行有效信息,一行为空行存储,所有下面两行写入一次修改信息
                            if i == 1 {
                                file.WriteAt([]byte(sid+"\n"), int64(long))
                                i++
                                break
                            }
                            if i == 3 {
                                file.WriteAt([]byte(sname+"\n"), int64(long))
                                i++
                                break
                            }
                            if i == 5 {
                                file.WriteAt([]byte(syears+"\n"), int64(long))
                                i++
                                break
                            }
                            if l == 1 && i == 7 { //老师的特殊修改
                                file.WriteAt([]byte(scount+"\n"), int64(long))
                                i++
                                break
                            }
                            if l == 2 && i == 7 { //学生的特殊修改
                                file.WriteAt([]byte(sgrade+"\n"), int64(long))
                                i++
                                break
                            }
                            if i == 6+m {
                                fmt.Println("修改成功")
                                return
                            }
                            i++
                            break
                        }
                    }
                    if err == io.EOF {
                        break
                    }
                }
            }
            fmt.Println("编号错误!") //全文件没找到输入的id,则输出编号错误
        }
      
        func (p *person) changePerson(per person, id int) {
            fmt.Println("请输入修改后的编号,姓名和年龄")
            fmt.Scanf("%d %s %d\n", &per.id, &per.name, &per.years) //获取
            p.SetPerson(per, true, id)
        }
      
    • 修改功能就这样了。
  5. 删除方法其实就是修改方法的简化版,不需要输入修改内容,直接根据id定位信息位置,然后用标准格式的空格覆盖对应位置就行了。

    • 代码如下:
    •   func deleteModel() {
            var id int
            var sid string
            fmt.Println("请输入一个编号")
            fmt.Scanf("%d\n", &id)   //获取id
            for n := 3; n > 0; n-- { //全文件循环寻找
                var str string
                var m, l int
                if n == 3 {
                    str = "person"
                    m = 0
                    l = 0
                } else if n == 2 {
                    str = "teacher"
                    m = 2
                    l = 1
                } else if n == 1 {
                    str = "student"
                    m = 2
                    l = 2
                } else {
                    return
                }
                filePath := "../" + str + ".txt" //打开相应文件
                file, err := os.OpenFile(filePath, os.O_RDWR, 0666)
                if err != nil {
                    fmt.Println("文件读取失败, error=", err)
                    return
                }
                defer file.Close()
                lens := 0
                long := 0
                i := 0
                reader := bufio.NewReader(file) //读取相应文件
                for {
                    s, err := reader.ReadString('\n') //一行一行获取文件内容
                    c := strings.Index(s, ":")
                    st := s[c+1:]
                    sid = fmt.Sprintf("%d", id) + "\n"
                    if st == sid { //判断文件中是否存在id与输入id相同
                        i = 1 //如果相同,储存相同信息
                    }
                    long = lens
                    lens += len(s)
                    if i > 0 {
                        for i <= 6+m { //循环,用标准空行覆盖对应信息
                            if i == 1 { //(由于不知道怎么删除,所有只能通过空格来覆盖了{不过这只影响打开文件查看,在此系统查看不受影响})
                                file.WriteAt([]byte("                                 \n"), int64(long))
                                i++
                                break
                            }
                            if i == 3 {
                                file.WriteAt([]byte("                                 \n"), int64(long))
                                i++
                                break
                            }
                            if i == 5 {
                                file.WriteAt([]byte("                                 \n"), int64(long))
                                i++
                                break
                            }
                            if l == 1 && i == 7 {
                                file.WriteAt([]byte("                                 \n"), int64(long))
                                i++
                                break
                            }
                            if l == 2 && i == 7 {
                                file.WriteAt([]byte("                                 \n"), int64(long))
                                i++
                                break
                            }
                            if i == 6+m {
                                fmt.Println("删除成功")
                                return
                            }
                            i++
                            break
                        }
                    }
                    if err == io.EOF {
                        break
                    }
                }
            }
            fmt.Println("编号错误!") //若全文件找不到id,则输出编号错误
        }
      
    • 删除功能就这样了。
  6. 退出方法不是有手就行?这里没什么好说了,加个循环处理,对应数字打破循环就行,代码就不放了。

4.总结

(感觉没啥好总结的) 这套代码主要是注重于文件处理操作,如果有谁文件处理操作不太行 (或是有自虐倾向) 可以试试写写这个系统 (保证你写到心累)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值