对文件的读写虽然看似很平常的功能,但对提高大型项目中的一些需要频繁修改的配置参数数据的存储的便捷性却起着非常大的作用,比如一些游戏项目中的战斗配置参数,任务配置参数等,一些需要不断调试修改,增加字段的配置表,我们可以直接将其存储在txt文件中或者lua脚本中,本文来一个存储在txt文件操作的示例。
一般这些数据在程序中是存储在结构体数组中的,我们读进来要将其解析存储到结构体的各个字段中,写的时候要把结构体的各个字段按格式写进文件中,为避免读写格式出错,减少读写的io操作次数,我们读写文件中的数据一般都是按行来读写,所以读的时候要把一行的字符串解析出各个字段,写的时候要把各个字段拼成一个字符串。下面我以个人信息(id,姓名,性别,年龄,身高,体重)为例写一个简单的增删改查的示例。个人信息文件如图1所示。
图1 人物信息文件
下面先定义结构体和结构体的方法,代码解释请看代码注释,有些不了解的接口函数可以复制一下百度,这是要慢慢积累的:
/*********************************
* File: peopleInfo.go
* Author: jay
* Date: 2018-10-13
**********************************/
package peopleInfo
import (
"bufio"
"errors"
"fmt"
"io"
"os"
"strconv"
"strings"
)
//个人信息结构体
type People_info struct {
Id_ int //个人id
Name_ string //个人姓名
Sex_ int //性别
Age_ int //年龄
Height_ int //身高(CM)
Weight_ int //体重(KG)
}
/***************************************
* People_info类型的方法
* 函数功能:从文件中数据中获取指定id的个人信息到People_info中
* 函数名:GetPepleCfgById
* 参数:个人id, 文件路径
* 返回值:错误码
***************************************/
func (p *People_info) GetPepleCfgById(id int, filePath string) (err error) {
var peoples People_infos
peoples.GetPepleInfoFromeTxt(filePath)
*p, err = peoples.GetPepleInfoById(id)
return
}
//所有人的数据存储结构
type People_infos struct {
Peoples []People_info //人物列表
PeopleNum int //人数
}
/***************************************
* People_infos类型的方法
* 函数功能:读取文件中所有的信息存进结构体
* 函数名:GetPepleInfoFromeTxt
* 参数:文件路径
* 返回值:错误码
***************************************/
func (p *People_infos) GetPepleInfoFromeTxt(filePath string) (err error) {
var people1 People_info
file, err1 := os.Open(filePath) //文件操作,打开文件
if err1 != nil {
err = err1 //文件打开出错
return
}
defer file.Close() //函数执行的最后关闭文件
br := bufio.NewReader(file)
p.PeopleNum = 0
for {
line, isPrefix, err1 := br.ReadLine() //按行读取
if err1 != nil {
if err1 != io.EOF {
err = err1 //读取出错
}
//读取到文件最后
break
}
if isPrefix { //一行数据字节太长
fmt.Println("A too long line, seems unexpected.")
return
}
if p.PeopleNum == 0 {
p.PeopleNum++ //统计人数
continue
}
str := strings.Fields(string(line)) //去掉空白字符切割成字符串数组
//对结构体赋值
people1.Id_, err = strconv.Atoi(str[0])
people1.Name_ = str[1]
people1.Sex_, err = strconv.Atoi(str[2])
people1.Age_, err = strconv.Atoi(str[3])
people1.Height_, err = strconv.Atoi(str[4])
people1.Weight_, err = strconv.Atoi(str[5])
p.Peoples = append(p.Peoples, people1)
p.PeopleNum++ //统计人数
}
return
}
/***************************************
* People_infos类型的方法
* 函数功能:将结构体数组数据全部写入文件中
* 函数名:SavePeopleInfoIntoTxt
* 参数:文件路径
* 返回值:错误码
***************************************/
func (p *People_infos) SavePeopleInfoIntoTxt(filePath string) (err error) {
file, err := os.Create(filePath) //创建文件
if err != nil {
fmt.Println("Failed to open the output file ", filePath)
return
}
defer file.Close()
tableHead := "ID\t姓名\t性别(0男,1女)\t年龄\t身高(cm)\t体重(kg)"
_, err = file.WriteString(tableHead + "\n") //按行把数据写进文件
if err != nil {
return
}
for _, peo := range p.Peoples {
str := strconv.Itoa(peo.Id_) + "\t" + peo.Name_ + "\t" + strconv.Itoa(peo.Sex_) + "\t" + strconv.Itoa(peo.Age_) + "\t" + strconv.Itoa(peo.Height_) + "\t" + strconv.Itoa(peo.Weight_)
_, err = file.WriteString(str + "\n") //按行把数据写进文件
if err != nil {
return
}
}
fmt.Println("Data save successful.")
return
}
/***************************************
* People_infos类型的方法
* 函数功能:从结构体数组数据中查找指定id的个人信息
* 函数名:GetPepleInfoById
* 参数:个人Id
* 返回值:单个人的信息People_info,错误码
***************************************/
func (p *People_infos) GetPepleInfoById(id int) (thisPeople People_info, err error) {
for _, p := range p.Peoples {
if p.Id_ == id {
thisPeople = p
return
}
}
err = errors.New("Can't not find this people info where id is : " + strconv.Itoa(id))
return
}
/***************************************
* People_infos类型的方法
* 函数功能:从结构体数组数据中查找指定名字的个人信息
* 函数名:GetPeopleInfoByName
* 参数:个人名字
* 返回值:对应名字的个人数据列表,错误码
***************************************/
func (p *People_infos) GetPeopleInfoByName(name string) (findPeoples People_infos, err error) {
for _, p := range p.Peoples {
if p.Name_ == name {
findPeoples.Peoples = append(findPeoples.Peoples, p)
findPeoples.PeopleNum++
}
}
if findPeoples.PeopleNum == 0 {
err = errors.New("Can't not find this people info where name is : " + name)
}
return
}
/***************************************
* People_infos类型的方法
* 函数功能:增加一个人的信息到People_infos结构体中
* 函数名:AddOnePeople
* 参数:个人信息结构体
* 返回值:错误码
***************************************/
func (p *People_infos) AddOnePeople(people People_info) (err error) {
_, err1 := p.GetPepleInfoById(people.Id_)
if err1 == nil {
err = errors.New("Id: " + strconv.Itoa(people.Id_) + " already exists.")
return
}
p.Peoples = append(p.Peoples, people)
p.PeopleNum++
return
}
/***************************************
* People_infos类型的方法
* 函数功能:从People_infos结构体中删除一个人的信息
* 函数名:RemoveOnePeople
* 参数:个人id
* 返回值:错误码
***************************************/
func (p *People_infos) RemoveOnePeople(id int) (err error) {
for i, pe := range p.Peoples {
if pe.Id_ == id {
p.Peoples = append(p.Peoples[:i], p.Peoples[i+1:]...)
return
}
}
err = errors.New("Id: " + strconv.Itoa(id) + " not exists.")
return
}
/***************************************
* People_infos类型的方法
* 函数功能:在People_infos结构体中修改一个人的信息,其中id不能修改
* 函数名:ReviseOnePeopleInfo
* 参数:一个人的信息结构体
* 返回值:错误码
***************************************/
func (p *People_infos) ReviseOnePeopleInfo(pe People_info) (err error) {
for i, pp := range p.Peoples {
if pp.Id_ == pe.Id_ {
p.Peoples[i].Name_ = pe.Name_
p.Peoples[i].Sex_ = pe.Sex_
p.Peoples[i].Age_ = pe.Age_
p.Peoples[i].Height_ = pe.Height_
p.Peoples[i].Weight_ = pe.Weight_
return
}
}
err = errors.New("People not exist | id= " + strconv.Itoa(pe.Id_))
return
}
/***************************************
* People_infos类型的方法
* 函数功能:该类的数据初始化
* 函数名:Init
* 参数:文件路径
* 返回值:错误码
***************************************/
func (p *People_infos) Init(filePath string) (err error) {
err1 := p.GetPepleInfoFromeTxt(filePath)
if err1 != nil {
err = errors.New(err1.Error() + "\nData Init failed.")
}
return
}
跟着套路走,就随便写个包测试代码吧:
/*********************************
* File: peopleInfo_test.go
* Author: jay
* Date: 2018-10-13
**********************************/
package peopleInfo
import (
"testing"
)
func TestOps(t *testing.T) {
filePath := "people_info.txt"
var p1 People_info
err1 := p1.GetPepleCfgById(18, filePath)
if err1 != nil {
t.Error("GetPepleCfgById failed.")
}
var p2 People_infos
err2 := p2.GetPepleInfoFromeTxt(filePath)
if err2 != nil {
t.Error("GetPepleInfoFromeTxt failed.")
}
var p3 People_infos
err3 := p3.Init(filePath)
if err3 != nil {
t.Error("Init failed.")
} else {
_, err4 := p3.GetPepleInfoById(18)
if err4 != nil {
t.Error("GetPepleInfoById failed.")
}
_, err5 := p3.GetPeopleInfoByName("jay")
if err5 != nil {
t.Error("GetPeopleInfoByName failed.")
}
var p4 People_info
p4.Id_ = 1001
p4.Age_ = 25
p4.Height_ = 180
p4.Name_ = "ABC"
p4.Sex_ = 1
p4.Weight_ = 55
p3.AddOnePeople(p4)
_, err6 := p3.GetPepleInfoById(p4.Id_)
if err6 != nil {
t.Error("AddOnePeople or GetPepleInfoById failed.")
}
p4.Name_ = "acc"
err7 := p3.ReviseOnePeopleInfo(p4)
if err7 != nil {
t.Error("ReviseOnePeopleInfo or AddOnePeople failed.")
}
p5, err8 := p3.GetPepleInfoById(p4.Id_)
if err8 != nil || p5.Name_ != p4.Name_ {
t.Error("ReviseOnePeopleInfo or GetPepleInfoById failed.")
}
p3.RemoveOnePeople(p4.Id_)
_, err9 := p3.GetPepleInfoById(p4.Id_)
if err9 == nil {
t.Error("RemoveOnePeople or GetPepleInfoById failed.")
}
err10 := p3.SavePeopleInfoIntoTxt(filePath)
if err10 != nil {
t.Error("SavePeopleInfoIntoTxt")
}
}
}
下面是main函数的代码:
/*********************************
* File: main.go
* Author: jay
* Date: 2018-10-13
**********************************/
package main
import (
"bufio"
"errors"
"fmt"
"os"
"peopleInfo"
"strconv"
"strings"
)
/***************************************
* 判错误函数
* 函数功能:判断操作返回的错误码,若有错误则退出程序
* 函数名:ifError
* 参数:错误码
* 返回值:无
***************************************/
func ifError(err error) {
if err != nil {
panic("\n" + err.Error())
}
}
/***************************************
* 判判断个人信息是否正确的函数
* 函数功能:判断所增加或者修改时输入的个人信息是否合理
* 函数名:ifInfoRight
* 参数:个人信息结构体
* 返回值:bool值, true表示正确,false表示错误
***************************************/
func ifInfoRight(p peopleInfo.People_info) (ron bool) {
if p.Id_ <= 0 {
return false
}
if p.Sex_ != 1 && p.Sex_ != 2 {
return false
}
if p.Age_ <= 0 || p.Age_ > 200 {
return false
}
if p.Height_ <= 0 || p.Height_ > 400 {
return false
}
if p.Weight_ <= 0 || p.Weight_ > 300 {
return false
}
return true
}
//程序主函数
func main() {
var pes peopleInfo.People_infos
filePath := "../people_info.txt" //文件的存放路径,相对路径
err1 := pes.Init(filePath) //初始化函数,即从文件中读取信息解析到结构体中
ifError(err1)
defer pes.SavePeopleInfoIntoTxt(filePath) //程序的最后需要把结构体写回到文件中进行保存
//提示操作命令
fmt.Println("Enter following commands to operate the people info database:\n" +
"list -- View the existing people info\n" +
"add -- Add a people to database\n" +
"remove -- Remove the specified people from database\n" +
"revise -- revise the specified people info in database\n" +
"find -- Find the specified people from database\n" +
"q or e --Exit the system")
r := bufio.NewReader(os.Stdin) //从标准输入中获取字符串
for {
var pe peopleInfo.People_info
var err error
fmt.Print("Enter command -> ")
rawLine, _, _ := r.ReadLine()
line := string(rawLine)
if line == "q" || line == "e" { //退出程序
break
}
tokens := strings.Split(line, " ") //切割字符串获取各个参数
switch tokens[0] {
case "list": //列出所有数据
{
for _, pe := range pes.Peoples {
fmt.Println(pe)
}
}
case "add": //增
{
if len(tokens) == 7 {
pe.Id_, err = strconv.Atoi(tokens[1])
pe.Name_ = tokens[2]
pe.Sex_, err = strconv.Atoi(tokens[3])
pe.Age_, err = strconv.Atoi(tokens[4])
pe.Height_, err = strconv.Atoi(tokens[5])
pe.Weight_, err = strconv.Atoi(tokens[6])
if !ifInfoRight(pe) {
err = errors.New("People info input error.")
break
}
err = pes.AddOnePeople(pe)
} else {
err = errors.New("Command error.")
}
}
case "remove": //删
{
if len(tokens) == 2 {
var id int
id, err = strconv.Atoi(tokens[1])
err = pes.RemoveOnePeople(id)
} else {
err = errors.New("Command error.")
}
}
case "revise": //改
{
if len(tokens) == 7 {
pe.Id_, err = strconv.Atoi(tokens[1])
pe.Name_ = tokens[2]
pe.Sex_, err = strconv.Atoi(tokens[3])
pe.Age_, err = strconv.Atoi(tokens[4])
pe.Height_, err = strconv.Atoi(tokens[5])
pe.Weight_, err = strconv.Atoi(tokens[6])
if !ifInfoRight(pe) {
err = errors.New("People info input error.")
break
}
err = pes.ReviseOnePeopleInfo(pe)
} else {
err = errors.New("Command error.")
}
}
case "find": //查
{
if len(tokens) == 3 {
if tokens[1] == "-i" {
var id int
id, err = strconv.Atoi(tokens[2])
pe, err = pes.GetPepleInfoById(id)
if err == nil {
fmt.Println(pe)
}
} else if tokens[1] == "-n" {
var ps peopleInfo.People_infos
ps, err = pes.GetPeopleInfoByName(tokens[2])
if err == nil {
for _, op := range ps.Peoples {
fmt.Println(op)
}
}
} else {
err = errors.New("Command error.")
}
} else {
err = errors.New("Command error.")
}
}
default:
{
err = errors.New("Command error.")
}
}
if err != nil {
fmt.Println(err)
}
}
return
}
PS:个人觉得程序的关键点主要在字符串的处理,虽然都有现成的函数,但至少你得知道有这样的函数才能去百度,然后使用。
github链接: https://github.com/lhj168os/peopleLib