【Go语言学习】——ini配置

本文介绍了如何使用Go语言处理ini配置文件,包括检测结构体类型、读取文件内容、遍历并解析数据,对不同格式的数据进行相应操作,如处理标题和键值对,将内容映射到结构体字段。

ini配置


  • 逻辑步骤

    1.检测传入的结构体是不是指针类型,值类型不能再函数中修改值

    2.读取ini文件的内容,并且处理成按行划分的切片

    3.遍历切片中每一组数据,分类处理:

    ​ 3.1 对于非格式化的数据直接报错

    ​ 3.2 对于[xxxx]标题内容,先判断格式,在读取内容,然后存储结构体名称xxxx

    ​ 3.3 对于K=V的内容,分别存储key和value,然后先去利用结构体名册读取到对应结构体,再在结构体中找到key’对应的结构体字段,最后对这个字段先判断类型然后赋值

  • 实现代码

    package main
    
    import (
    	"errors"
    	"fmt"
    	"io/ioutil"
    	"reflect"
    	"strconv"
    	"strings"
    )
    
    // Mysql配置结构体
    type MysqlConfig struct {
    	Address  string `ini:"address"`
    	Port     int    `ini:"port"`
    	Username string `ini:"username"`
    	Password string `ini:"password"`
    }
    
    // Redis配置结构体
    type RedisConfig struct {
    	Host     string `ini:"host"`
    	Port     int    `ini:"port"`
    	Password string `ini:"password"`
    	Database int    `ini:"database"`
    	Test     bool   `ini:"test"`
    }
    
    // 嵌套结构,包装所有的类型的配置结构体
    type Config struct {
    	MysqlConfig `ini:"mysql"`
    	RedisConfig `ini:"redis"`
    }
    
    func loadIni(fileName string, data interface{}) (err error) {
    	// 0.参数校验:data必须是结构体指针类型
    	t := reflect.TypeOf(data)
    	// 检测参数是否是一个指针
    	if t.Kind() != reflect.Ptr {
    		err = errors.New("data param should be a pointer")
    		return
    	}
    	// 检测指针是否指向结构体
    	if t.Elem().Kind() != reflect.Struct {
    		err = errors.New("data param should be a struct pointer")
    		return
    	}
    	// 1.读文件获取字节类型的数据
    	b, err := ioutil.ReadFile(fileName)
    	if err != nil {
    		err = errors.New("read file failed")
    		return
    	}
    	// 将结果转换为字符串再按照分行符进行切割
    	lineSlice := strings.Split(string(b), "\r\n")
    	// 2.利用循环一行行的读取数据
    	var structName string
    	for idx, line := range lineSlice {
    		// 去掉字符串中的空格
    		line = strings.TrimSpace(line)
    		// 跳过中间的空行
    		if len(line) == 0 {
    			continue
    		}
    		// 2.1 如果是注释就跳过这一行
    		if strings.HasPrefix(line, ";") || strings.HasPrefix(line, "#") {
    			continue
    		}
    		// 2.2 如果是[开头的就是一节的开始
    
    		if strings.HasPrefix(line, "[") {
    			// 检查是不是以]结尾
    			if !strings.HasPrefix(line, "[") || !strings.HasSuffix(line, "]") {
    				// if line[0] != '[' || line[len(line)-1] != ']' {
    				err = fmt.Errorf("line:%d syntax error", idx+1)
    				return
    			}
    			// 通过消除[]查里面的空格检验长度来看[]里面的内容是不是为空
    			sectionName := strings.TrimSpace(line[1 : len(line)-1])
    			if len(sectionName) == 0 {
    				err = fmt.Errorf("line:%d syntax error", idx+1)
    				return
    			}
    			// 根据字符串sectionName去data里面根据反射找到对应的结构体
    			// 注意t是指针,记得取Elem才能取值
    			for i := 0; i < t.Elem().NumField(); i++ {
    				field := t.Elem().Field(i)
    				if sectionName == field.Tag.Get("ini") {
    					// 找到对应字段名
    					structName = field.Name
    					// fmt.Printf("找到%s对应的嵌套结构体%s\n", sectionName, structName)
    
    				}
    			}
    		} else {
    			// 2.3 如果不是[就是=分割的键值对
    			// 通过等号分割这一行,等号左边是key,右边是value
    			// 判断格式
    			if !strings.Contains(line, "=") || strings.HasPrefix(line, "=") {
    				err = fmt.Errorf("line:%d syntax error", idx+1)
    				return
    			}
    			index := strings.Index(line, "=")
    			// 获得键和值,并且消除中间的空格
    			key := strings.TrimSpace(line[:index])
    			value := strings.TrimSpace(line[index+1:])
    			// 根据structName取出嵌套在data里面的结构体
    			v := reflect.ValueOf(data)
    			sValue := v.Elem().FieldByName(structName)
    			sType := sValue.Type()
    			if sType.Kind() != reflect.Struct {
    				err = fmt.Errorf("data中的%s应该是一个结构体", structName)
    				return
    			}
    			var fieldName string
    			var fileType reflect.StructField
    			// 遍历嵌套结构体的每一个字段,判断tag是否等于key
    			for i := 0; i < sType.NumField(); i++ {
    				filed := sType.Field(i)
    				fileType = filed
    				if filed.Tag.Get("ini") == key {
    					fieldName = filed.Name
    					break
    				}
    
    			}
    			//如相等,则给这个字段赋值.通过fieldName取出字段然后赋值
    			// 先判断结构体有没有对应字段
    			if len(fieldName) == 0 {
    				continue
    			}
    			// 若有在根据字段取出结构体的字段
    			fileobj := sValue.FieldByName(fieldName)
    			// filetype, _ := sType.FieldByName(fieldName)
    			// fmt.Println(fieldName, fileObj.Type.Kind())
    			// fmt.Println(fieldName, fileType.Type.Kind())
    			switch fileType.Type.Kind() {
    			// switch filetype.Type.Kind() {
    			case reflect.String:
    				fileobj.SetString(value)
    			case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
    				var valueInt int64
    				// 利用好strconv.ParseXXX转换值类型
    				valueInt, err = strconv.ParseInt(value, 10, 64)
    				if err != nil {
    					err = fmt.Errorf("line:%d value type error", idx+1)
    					return
    				}
    				fileobj.SetInt(valueInt)
    			case reflect.Bool:
    				var valueBool bool
    				valueBool, err = strconv.ParseBool(value)
    				if err != nil {
    					err = fmt.Errorf("line:%d value type error", idx+1)
    					return
    				}
    				fileobj.SetBool(valueBool)
    			case reflect.Float32, reflect.Float64:
    				var valueFloat float64
    				valueFloat, err = strconv.ParseFloat(value, 64)
    				if err != nil {
    					err = fmt.Errorf("line:%d value type error", idx+1)
    					return
    				}
    				fileobj.SetFloat(valueFloat)
    			}
    
    		}
    
    	}
    
    	return
    }
    
    func main() {
    	var cfg Config
    	// 因为要赋值所以传递的是指针
    	err := loadIni("./conf.ini", &cfg)
    	if err != nil {
    		fmt.Printf("load ini failed , err:%v\n", err)
    		return
    	}
    	fmt.Printf("%#v", cfg)
    }
    
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值