配置文件库项目

简介

  • 用途: 解析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
}

测试用例

  • 测试代码不需要和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\n",err)
      return
   }
   fmt.Printf("conf=%#v\n",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