配置文件库项目
简介
- 用途: 解析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),"\n")
lineNo := 0
for _,line := range lines {
lineNo ++
//去掉首位空串
line = strings.Trim(line,"\t\n\r")
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 0 {
sectionName = tField.Tag.Get("ini")
}
//使用[]拼接sectionName
sectionName = fmt.Sprintf("[%s]\n",sectionName)
//保存sectionName信息
strSlice = append(strSlice,sectionName)
for j:=0;j 0 {
subTFieldName = subTFieldTagName
}
//2. 拿到值信息
subVField := vField.Field(j)
fieldStr := fmt.Sprintf("%s=%v\n",subTFieldName,subVField.Interface())
//fmt.Printf("conf%s\n",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
}
测试用例
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\n",err)
return
}
fmt.Printf("conf=%#v\n",config)
oconfig.MarshalFile("./aaa.ini",config)
}
;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