Go语言(十七) 配置文件库项目
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go语言(十七) 配置文件库项目相关的知识,希望对你有一定的参考价值。
配置文件库项目简介
- 用途: 解析ini类型的配置文件
- 知识点: 主要基于反射
- ini配置文件的解析,配置文件如下
;config file
[server]
host = www.baidu.com
port = 8080
[cartdb]
user = root.xxx
password = root
host = localhost
port = 3306
database = cartdb
rate = 1.2
需求分析
- 拆离出动态配置
- 映射数据到项目数据结构中
-包含配置文件读取(UnMarshalFile)和写入(MarsharFile)两个功能
代码部分
package oconfig
import (
"fmt"
"io/ioutil"
"reflect"
"strconv"
"strings"
)
func UnMarshal(data []byte,result interface{}) (err error) {
t := reflect.TypeOf(result)
v := reflect.ValueOf(result)
_ = v
kind := t.Kind()
//输入校验
if kind != reflect.Ptr {
panic("please input a address")
}
//解析配置文件
var sectionName string
lines := strings.Split(string(data),"
")
lineNo := 0
for _,line := range lines {
lineNo ++
//去掉首位空串
line = strings.Trim(line,"
")
if len(line) == 0 {
//空行处理
continue
}
//去掉注释
if line[0] == ‘#‘ || line[0] == ‘;‘ {
continue
}
// 解析group
if line[0] == ‘[‘ {
//解析group的名称,是否以[]包含校验
if len(line)<=2 || line[len(line)-1] != ‘]‘ {
tips := fmt.Sprintf("syntax err,invalid section:"%s",line:%d",line,lineNo)
panic(tips)
}
//获取group名称,去空格
sectionName = strings.TrimSpace(line[1:len(line)-1])
if len(sectionName) == 0 {
tips := fmt.Sprintf("syntax err,invalid section:"%s",line:%d",line,lineNo)
panic(tips)
}
}else {
//group的字段
if len(sectionName) == 0 {
tips := fmt.Sprintf("key-value:%s 不属于任何group,lineNo:%d",line,lineNo)
panic(tips)
}
//找到第一个等于号所在的位置
index := strings.Index(line,"=")
if index == -1 {
//等号之前没有配置的异常
tips := fmt.Sprintf("syntax error,not found =,line:%s,lineNo:%d",line,lineNo)
panic(tips)
}
key := strings.TrimSpace(line[0:index]) //去掉空格
value := strings.TrimSpace(line[index+1:]) //获取到的value
if len(key) == 0 {
//key不存在异常
tips := fmt.Sprintf("syntax error,not found key,line:%s,lineNo:%d",line,lineNo)
panic(tips)
}
//通过Config找到所属的SectionName配置,需要查找两遍
//1.找到sectionName在result中对应的结构体s1
for i := 0;i < t.Elem().NumField();i++ {
tfield := t.Elem().Field(i)
vField := v.Elem().Field(i)
if tfield.Tag.Get("ini") != sectionName {
continue
}
//2.通过当前解析的key,找到对应的结构体s1中的对应字段
tfieldType := tfield.Type
if tfieldType.Kind() != reflect.Struct {
tips := fmt.Sprintf("field %s is not a struct",tfield.Name)
panic(tips)
}
//查找子结构体中的数据
for j:=0;j<tfieldType.NumField();j++ {
//找不到key对应field的时候就跳过
tKeyField := tfieldType.Field(j)
vKeyField := vField.Field(j)
if tKeyField.Tag.Get("ini") != key {
continue
}
//找到了对应的字段,并将值映射到对应的字段
switch tKeyField.Type.Kind() {
case reflect.String:
vKeyField.SetString(value)
case reflect.Int,reflect.Uint,reflect.Int16,reflect.Uint16:
fallthrough //穿透到下个case
case reflect.Int32,reflect.Uint32,reflect.Int64,reflect.Uint64:
valueInt,err := strconv.ParseInt(value,10,64) //十进制64字节int
if err != nil {
tips := fmt.Sprintf("value %s is not a convert to int,lineNo:%d",value,lineNo)
panic(tips)
}
vKeyField.SetInt(valueInt)
case reflect.Float32,reflect.Float64:
valueFloat,err := strconv.ParseFloat(value,64)
if err != nil {
tips := fmt.Sprintf("value %s is not a convert to float,lineNo:%d",value,lineNo)
panic(tips)
}
vKeyField.SetFloat(valueFloat)
default:
tips := fmt.Sprintf("key "%s" is not a convert to %v,lineNo:%d",key,tKeyField.Type.Kind(),lineNo)
panic(tips)
}
}
break
}
}
}
return
}
func UnMarshalFile(filename string,result interface{}) (err error) {
//读取配置文件
data,err := ioutil.ReadFile(filename)
if err != nil {
return
}
return UnMarshal(data,result)
}
func Marshal(result interface{}) (data []byte,err error) {
//序列化用户数据
t := reflect.TypeOf(result)
v := reflect.ValueOf(result)
if t.Kind() != reflect.Struct {
panic("please input struct type")
}
var strSlice []string
//遍历结构体中的字段
for i:=0;i<t.NumField();i++ {
tField := t.Field(i)
vField := v.Field(i)
if tField.Type.Kind() != reflect.Struct {
continue //非结构体跳过
}
//sectionName的获取
sectionName := tField.Name
//subTFieldTagName := subTField.Tag.Get("ini")
if len(tField.Tag.Get("ini")) > 0 {
sectionName = tField.Tag.Get("ini")
}
//使用[]拼接sectionName
sectionName = fmt.Sprintf("[%s]
",sectionName)
//保存sectionName信息
strSlice = append(strSlice,sectionName)
for j:=0;j<tField.Type.NumField();j++ {
//1. 拿到类型信息
subTField := tField.Type.Field(j) //获取字段名称
if subTField.Type.Kind() == reflect.Struct || subTField.Type.Kind() == reflect.Ptr{
continue //跳过结构体或者指针类型,正常是key=value格式
}
subTFieldName := subTField.Name
subTFieldTagName := subTField.Tag.Get("ini")
if len(subTFieldTagName) > 0 {
subTFieldName = subTFieldTagName
}
//2. 拿到值信息
subVField := vField.Field(j)
fieldStr := fmt.Sprintf("%s=%v
",subTFieldName,subVField.Interface())
//fmt.Printf("conf%s
",fieldStr)
strSlice = append(strSlice,fieldStr) //保存sectionName对应的字段
}
}
for _, v := range strSlice {
//展开slice的结果
data = append(data,[]byte(v)...)
}
return
}
func MarshalFile(filename string,result interface{}) (err error) {
//将用户输入的数据写入配置文件
data,err := Marshal(result)
if err != nil {
return
}
err = ioutil.WriteFile(filename,data,0755)
return
}
测试用例
- 测试代码不需要和oconfig处于同一个包
package main
import (
"fmt"
"oldBoy/oconfig"
)
type Config struct {
Server ServerConf `ini:"server"`
CartDb DbConf `ini:"cartdb"`
}
type ServerConf struct {
Host string `ini:"host"`
Port int `ini:"port"`
}
type DbConf struct {
User string `ini:"user"`
Password string `ini:"password"`
Host string `ini:"host"`
Port int `ini:"port"`
Database string `ini:"database"`
Rate float32 `ini:"rate"`
}
func main() {
var config Config
filename := "./example.ini"
//config要传入地址(引用类型)
err := oconfig.UnMarshalFile(filename,&config)
if err != nil {
fmt.Printf("unmarshal file faild,err:%v
",err)
return
}
fmt.Printf("conf=%#v
",config)
oconfig.MarshalFile("./aaa.ini",config)
}
- example.ini (测试配置文件)
;config file
[server]
host = www.baidu.com
port = 8080
#DB Config
[cartdb]
user = root.xxx.sss
password = root
host = localhost
port = 3306
database = cartdb
rate = 1.23456
以上是关于Go语言(十七) 配置文件库项目的主要内容,如果未能解决你的问题,请参考以下文章
Golang✔️走进 Go 语言✔️ 第十七课 select & 超时和非阻塞