美文网首页
Go语言基础练习六

Go语言基础练习六

作者: 羋学僧 | 来源:发表于2022-01-13 17:13 被阅读0次

    ini配置文件解析

    conf.ini

    ; mysql config
    [mysql]
    address=10.20.30.40
    port=3306
    username=root
    password=rootroot
    
    # redis config
    [redis]
    host=10.20.30.40
    port=6379
    password=root123
    database=0
    test=false
    

    main.go

    package main
    
    import (
        "errors"
        "fmt"
        "io/ioutil"
        "reflect"
        "strconv"
        "strings"
    )
    
    // ini配置文件解析器
    
    // MySQLConfig MySQL配置结构体
    type MySQLConfig struct {
        Address  string `ini:"address"`
        Port     int    `ini:"port"`
        Username string `ini:"username"`
        Password string `ini:"password"`
    }
    
    // RedisConfig Redis配置结构体
    type RedisConfig struct {
        Host     string `ini:"host"`
        Port     int    `ini:"port"`
        Password string `ini:"password"`
        Database string `ini:"database"`
        Test     bool   `ini:"test"`
    }
    
    type Config struct {
        MySQLConfig `ini:"mysql"`
        RedisConfig `ini:"redis"`
    }
    
    func loadIni(fileName string, data interface{}) (err error) {
        // 0. 参数的校验
        // 0.1 传进来的data参数必须是指针类型(因为需要在函数中对其赋值)
        t := reflect.TypeOf(data)
        if t.Kind() != reflect.Ptr {
            err = errors.New("data param should be a pointer") // 创建一个错误
            return
        }
        // 0.2 传进来的data参数必须是结构体类型指针(因为配置文件中各种键值对,需要赋值给结构体的字段)
        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 {
            return
        }
        // string(b) 将字节类型的文件内容转换成字符串
        lineSlice := strings.Split(string(b), "\r\n")
        fmt.Println(lineSlice)
        // 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 如果是 [  开头的,就表示是节(section)
            if strings.HasPrefix(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(strings.TrimSpace(line[1:len(line)-1])) == 0 {
                    err = fmt.Errorf("line:%d syntax error", idx+1)
                    return
                }
                // 根据字符串sectionName去data里面根据反射找到对应的结构体
                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 如果不是 [ 开头的,就是 = 分割的键值对
                // 2.3.1 根据等号分割这一行,等号左边是key,等号右边是value
                if strings.Index(line, "=") == -1 || strings.HasPrefix(line, "=") {
                    err = fmt.Errorf("lien:%d syntax error", idx+1)
                    return
                }
                index := strings.Index(line, "=")
                key := strings.TrimSpace(line[:index])
                value := strings.TrimSpace(line[index+1:])
                // 2.3.2 根据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 fieldType reflect.StructField
                // 2.3.3 遍历嵌套结构体的每一个字段,判断tag是不是等于key
                for i := 0; i < sValue.NumField(); i++ {
                    filed := sType.Field(i) // tag信息是存储在类型信息中的
                    fieldType = filed
                    if filed.Tag.Get("ini") == key {
                        // 找到对应的字段
                        fieldName = filed.Name
                        break
                    }
    
                }
                // 2.3.4 如果key = tag,给这个字段赋值
                // 2.3.4.1 根据fieldName 去取出这个字段
                if len(fieldName) == 0 {
                    // 在结构体中找不到对应的字符
                    continue
                }
                fileObj := sValue.FieldByName(fieldName)
                // 2.3.4.2 对其赋值
                switch fieldType.Type.Kind() {
                case reflect.String:
                    fileObj.SetString(value)
                case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
                    var valueInt int64
                    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 faild, err:%v", err)
            return
        }
        fmt.Printf("%#v\n", cfg)
    }
    
    

    相关文章

      网友评论

          本文标题:Go语言基础练习六

          本文链接:https://www.haomeiwen.com/subject/htmncrtx.html