嵌入式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()
}
- 微信小程序「学科排名」发布了
- Nginx 负载均衡的Cache缓存批量清理的操作记录
- DotNet软件开发框架
- Nginx通过https方式反向代理的简单实现
- 再论IBatisNet + Castle进行项目的开发
- 利用xml轻松读取web.config中的用户自定义节
- 分布式监控系统Zabbix-3.0.3-完整安装记录(3)-监控nginx,php,memcache,Low-level discovery磁盘IO
- python报错问题解决:'ascii' codec can't encode character
- 利用message queue实现aspx与winform通信, 并附完整示例
- 10招步骤保护IIS服务器安全
- Haproxy+Keepalived高可用环境部署梳理(主主和主从模式)
- Android-Universal-Image-Loader 图片异步加载类库的使用
- 工作组模式下专用队列(Private Queue)如何引用远程队列路径
- haproxy反向代理环境部署(http和https代理)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Struts2第一天Struts2的概述,Struts2的入门,Struts2常见的配置、Struts2的Action的编写
- SQL 求平均值时去掉极值
- Go 每日一库之 jennifer
- Go 每日一库之 go-cmp
- 孤立森林(Isolation Forest)
- Go 每日一库之 buntdb
- SQL 将多列的数据转到一列
- Go 每日一库之 gjson
- Go 每日一库之 sjson
- SQL 计算累积销售额
- Go 每日一库之 jj
- Struts2第二天:Struts2的数据的封装、结果页面配置
- Go 每日一库之 govaluate
- Go 每日一库之 validator
- Go 每日一库之 gopsutil