嵌入式linux之go语言开发(十二)参数配置文件存储模块开发

时间:2022-07-22
本文章向大家介绍嵌入式linux之go语言开发(十二)参数配置文件存储模块开发,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

参数配置和存储是经常用到的功能。

比如常用的可以用json文件,XML文件,或INI文件,YAML文件,properties文件等存储配置信息。

但是,这些都不够简单。

我想要的简单有多简单呢?整个互联网上没有比这更简单好用的。

例如,我想保存个IP地址和端口参数。理想是这样的:

syscfg.ip = "218.28.133.181"

syscfg.port = 22288

syscfg.saveCfg()

就这几步,就把需要的参数持久化的存储起来了。开机只需要syscfg.load() 就完成了配置信息的加载。

比起其他的方式,是不是没有比这更简单了。即便是小白,立马也会用。

之前在Android和嵌入式c上,都已封装好的有这样的功能很好用。

Android上的封装,参见:https://blog.csdn.net/yyz_1987/article/details/104122764

但是在go语言这块儿,计划也打造一个这样的参数配置存储的功能。

即使其他维护的人不懂yaml,不懂xml,不懂json解析,没关系,这种封装后谁都很快会用。

这样的好处是什么呢?这样实际上也是一种模块化和分层的思想。让应用层的人不关心底层是如何实现的。

有的说直接操作json或gob也很简单啊,几行代码而已。但是,如果哪天说想让你换种存储方式,应用里每处要扒拉改一遍吗?

再比如假如对配置文件分分类,有N多个配置文件要存储,要写N遍的解析json,加载json的方法吗?

好的封装可以做到事半功倍。

以下为简单的实现:

代码看着是有点儿长,其实这里面BaseConf以及它的相关操作代码可以单独抽出来。

package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"reflect"
	"strings"
)

// BaseConf 配置文件操作的基础封装
type BaseConf struct {
}

// OthCfg 配置文件1
type OthCfg struct {
	BaseConf
	IP   string
	Port int
}

// SysCfg 配置文件2
type SysCfg struct {
	BaseConf
	Name string
	Age  int
}

// Save ...
func (cfg *BaseConf) Save(v interface{}) {

	t := reflect.TypeOf(v)
	fname := fmt.Sprintf("%s.json", t)
	fname = strings.Replace(fname, "*", "r", -1)
	fmt.Println(t)
	fmt.Println(fname)
	//fmt.Printf("type of is:%vn", t)

	jsond, err := json.Marshal(v)
	if err != nil {
		fmt.Println("生成json字符串错误")
	}
	fmt.Println(string(jsond))
	SaveFile(fname, jsond)

}

// Load ...
func (cfg *BaseConf) Load(v interface{}) {
	t := reflect.TypeOf(v)
	fname := fmt.Sprintf("%s.json", t)
	fname = strings.Replace(fname, "*", "r", -1)
	data, err := ReadFile(fname)
	if err != nil {
		fmt.Println(err)
		return
	}
	err = json.Unmarshal(data, v)
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(v)
}

// SaveFile 存文件
func SaveFile(filename string, data []byte) error {
	fi, err := os.Create(filename)
	if err != nil {
		fmt.Println(err)
		return err
	}
	defer fi.Close()
	w := bufio.NewWriter(fi)
	w.WriteString(string(data))
	w.Flush()
	return nil
}

// ReadFile 读文件
func ReadFile(filename string) ([]byte, error) {
	fi, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer fi.Close()
	r := bufio.NewReader(fi)
	var data []byte
	buf := make([]byte, 1024)
	for {
		n, err := r.Read(buf)
		if err != nil && err != io.EOF {
			return nil, err
		}
		if 0 == n {
			break
		}
		data = append(data, buf[:n]...)
	}
	return data, nil

}

var sysCfg SysCfg

func main() {

	fmt.Println("Hello Go")

	sysCfg.Load(&sysCfg)

	//sysCfg.Name = "yang"
	//sysCfg.Age = 21

	//sysCfg.Save(&sysCfg)

	oth := OthCfg{}
	oth.IP = "192.168.1.97"
	oth.Port = 5057
	oth.Save(&oth)
}

而实际使用者有多简单呢?不用关系底层是什么存储,文件是什么名字。

使用者,即便有多个配置文件要存储,只需要像这样子:

package main
import (
	"fmt"
    "testconfig/config"
)

// OthCfg 配置文件1
type OthCfg struct {
	BaseConf
	IP   string
	Port int
}

// SysCfg 配置文件2
type SysCfg struct {
	BaseConf
	Name string
	Age  int
}

var sysCfg SysCfg

func main() {

	fmt.Println("Hello Go")

	//sysCfg.Load()

	sysCfg.Name = "yang"
	sysCfg.Age = 21

	sysCfg.Save(&sysCfg)

	oth := OthCfg{}
	oth.IP = "192.168.1.97"
	oth.Port = 5057
	oth.Save(&oth)
}

唯一有点儿遗憾的是,你在操作Save的时候,必须把对应的自己传进去才行,不能传错了。

比如oth.Save(&oth) ,若搞成 oth.Save(&sysCfg)会是什么情况?

有没更好的办法?让实例化时就传进来呢?让使用者可以直接调用个oth.Save()即可。

有办法吗?欢迎讨论

最后感谢网友雨痕大神给出的解决方案。

雨痕,《go语言学习笔记》一书的作者。在这里推荐下他的书。真不错,讲的很细致。

他的解决办法如下:

另外发现一很有意思的事情,,有人知道原因吗?

如果把里面conf改为大写,则不行,还必须用小写 sysCfg.BaseConf.conf ,里面是 小写的conf正确 一个大小写,区别就这么大。

有时候学习劲头来了,挡都挡不住,本来问题解决了,但我就想搞明白,刨根究底。这问题影响我晚上没睡好觉。

不过最后还是想明白了。其实那地方为什么必须是小写,原因很简单啊,并没有什么玄机。

最后放出运行没问题的代码:

package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"reflect"
	"strings"
)

// IConf 组合模式,父类 定义一个 接口
type IConf interface{
	Save()
	Load()
}
// BaseConf 配置文件操作的基础封装
type BaseConf struct {
	conf IConf
}

// OthCfg 配置文件1
type OthCfg struct {
	BaseConf
	IP   string
	Port int
}

// SysCfg 配置文件2
type SysCfg struct {
	BaseConf
	Name string
	Age  int
}

// Save ...
func (cfg *BaseConf) Save() {

	t := reflect.TypeOf(cfg.conf)
	fname := fmt.Sprintf("%s.json", t)
	fname = strings.Replace(fname, "*", "r", -1)
	fmt.Println(t)
	fmt.Println(fname)
	//fmt.Println(cfg.Conf)
	//fmt.Printf("type of is:%vn", t)

	jsond, err := json.Marshal(cfg.conf)
	if err != nil {
		fmt.Println("生成json字符串错误")
	}
	fmt.Println(string(jsond))
	SaveFile(fname, jsond)

}

// Load ...
func (cfg *BaseConf) Load() {
	t := reflect.TypeOf(cfg.conf)
	fname := fmt.Sprintf("%s.json", t)
	fname = strings.Replace(fname, "*", "r", -1)
	fmt.Println(cfg.conf)
	data, err := ReadFile(fname)
	if err != nil {
		fmt.Println(err)
		return
	}
	err = json.Unmarshal(data, cfg.conf)
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(cfg)
}

// SaveFile 存文件
func SaveFile(filename string, data []byte) error {
	fi, err := os.Create(filename)
	if err != nil {
		fmt.Println(err)
		return err
	}
	defer fi.Close()
	w := bufio.NewWriter(fi)
	w.WriteString(string(data))
	w.Flush()
	return nil
}

// ReadFile 读文件
func ReadFile(filename string) ([]byte, error) {
	fi, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer fi.Close()
	r := bufio.NewReader(fi)
	var data []byte
	buf := make([]byte, 1024)
	for {
		n, err := r.Read(buf)
		if err != nil && err != io.EOF {
			return nil, err
		}
		if 0 == n {
			break
		}
		data = append(data, buf[:n]...)
	}
	return data, nil

}

var sysCfg SysCfg

func main() {

	fmt.Println("Hello Go")
	//sysCfg.BaseConf.Conf = &sysCfg
	//sysCfg.Load()
	//fmt.Println(sysCfg)
	sysCfg.BaseConf.conf = &sysCfg
	sysCfg.Name = "yang"
	sysCfg.Age = 188
	sysCfg.Save()
	sysCfg.Load()
	fmt.Println(sysCfg)
	//sysCfg.Save(&sysCfg)

	// oth := OthCfg{}
	// oth.Conf = &oth
	// oth.IP = "192.168.1.97"
	// oth.Port = 5057
	
	// oth.Save()
}