服务计算作业四——程序包开发,读简单配置文件 v1

it2023-01-23  53

任务目标

熟悉程序包的编写习惯(idioms)和风格(convetions)熟悉 io 库操作使用测试驱动的方法简单 Go 程使用事件通知

任务要求

核心任务:包必须提供一个函数 Watch(filename,listener) (configuration, error)包必须包括以下内容: 生成的中文 api 文档 有较好的 Readme 文件,包括一个简单的使用案例 每个go文件必须有对应的测试文件 必须提供自定义错误 使有 init 函数,使得 Unix 系统默认采用 # 作为注释行,Windows 系统默认采用 ; 作为注释行。不能使用第三方包,但可以参考、甚至复制它们的代码。

实现

包的结构

init函数:

func Init() { if runtime.GOOS == "linux" { comment = '#' } else if runtime.GOOS == "windows" { comment = ';' } //fmt.Println(runtime.GOOS) }

通过runtime.GOOS得到操作系统类型从而对注释符进行初始化。

listen:

start_file_mes := getFileMes(file_name) flag := false for { cur_file_mes := getFileMes(file_name) len1 := len(start_file_mes) len2 := len(cur_file_mes) if len1 != len2 { break } for i := 0; i < len1; i++ { if start_file_mes[i] != cur_file_mes[i] { flag = true break } } if flag { break } }

通过保存一开始的文件内容,并不断轮询读取文件内容比较,若有不同则返回。 其中用到的getFileMes如下:

func getFileMes(file_name string) []string { var file_mes []string file, err := os.Open(file_name) if err != nil { fmt.Println("Get file message error!") return file_mes } reader := bufio.NewReader(file) for { str, err := reader.ReadString('\n') if err != nil { break } file_mes = append(file_mes, str) } file.Close() return file_mes }

自定义错误类型:

没有想到要添加什么信息,因此就只有一个string和一个int。

read_file函数:

func read_file(file_name string) (map[string]string, error_mes) { var error error_mes file, err := os.Open(file_name) configuration := make(map[string]string) if err != nil { error = get_new_error("open ili file error.", 0) return configuration, error } else { error = get_new_error("", 1) } defer file.Close() reader := bufio.NewReader(file) for { //逐行读取 str, err := reader.ReadString('\n') if err != nil && err != io.EOF { fmt.Println(err) break } //删去空格 str = strings.TrimSpace(str) //如果是空的/注释/则忽略 if str == "" || str[0] == '[' || str[0] == comment { continue } pair := strings.Split(str, "=") if len(pair) == 2 { key := strings.TrimSpace(pair[0]) value := strings.TrimSpace(pair[1]) configuration[key] = value // fmt.Println(key) } if err != nil { // fmt.Println(err) break } } return configuration, error }

含义如注释所示。

有了上面这些函数,实现watch就简单了。

func Watch(file_name string, Listen Listener) (map[string]string, error_mes) { Init() config, err := Read_file(file_name) if len(err.mes) != 0 { fmt.Println(err.mes) return config, err } fmt.Println("Listening....") Print_config(config) Listen.listen(file_name) fmt.Println("The file have been changed.") fmt.Println("") config, err = Read_file(file_name) if len(err.mes) != 0 { fmt.Println(err.mes) return config, err } //Print_config(config) return config, err }

其中print_config是打印出config的函数。

main函数:

package main import ( watch "github.com/USER/watch/watch" ) func main() { var listener watch.Listener listener = watch.ListenFunc(func(string) {}) var filename = "example.ini" watch.Watch(filename, listener) }

实验结果

main

example函数为网站给的样例。运行结果如下: 如果修改该文件,则会出现提示语句,然后打印一次新增加的内容,如新增加aaa=bbb:

利用go test进行检验

由于watch函数的运行需要用到监听器listener,不方便检验,而watch函数中最核心的就是read_file函数,它将文件读取并识别参数以map[string]string返回,因此我们对这个函数进行检验即可。 在watch.go同文件夹内编写read_file_test.go:

package watch import ( "reflect" "testing" ) func TestInit(t *testing.T) { Init() } func TestReadFile(t *testing.T) { filename := "example.ini" receive, _ := Read_file(filename) //Print_config(receive) expect := map[string]string{ "app_mode": "development", "data": "/home/git/grafana", } if !reflect.DeepEqual(receive, expect) { t.Errorf("expect:") } }

这里用到的DeepEqual是可以用于检测map是否相等的库函数。 运行结果: 生成中文API 通过apidoc工具可以在编写代码时通过加入注释的方式来生成API,具体参考apiDoc - 超简单的文档生成器。 生成效果:

具体可以下载源代码打开查看。

源代码

传送门:GitHub

最新回复(0)