-
前言:
手写GO用反射读取ini配置文件。
-
实例代码:
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 ... type RedisConfig struct { Host string `ini:"host"` Port int `ini:"port"` Password string `ini:"password"` Database int `ini:"database"` Test bool `ini:"test"` } // Config ... type Config struct { MysqlConfig `ini:"mysql"` RedisConfig `ini:"redis"` } // 读取INI方法 func loadIni(fileName string, data interface{}) (err error) { t := reflect.TypeOf(data) fmt.Println(t, t.Kind()) 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 } b, err := ioutil.ReadFile(fileName) if err != nil { return } lineSlice := strings.Split(string(b), "\r\n") var structName string for idx, line := range lineSlice { // 去掉字符串首尾的空格 line = strings.TrimSpace(line) // 如果是空行就跳过 if len(line) == 0 { continue } // 如果是注释就跳过 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(sectionName) == 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 { // 如果不是[开头就是=分割的键值对 // 以等号分割这一行,等号左边是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:]) // 根据strucrName 去 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 < sValue.NumField(); i++ { filed := sType.Field(i) // tag信息是存储在类型信息中的 if filed.Tag.Get("ini") == key { // 找到对应的字段 fieldName = filed.Name fileType = filed break } } // 如果key = tag,给这个字段赋值 // 根据fieldName 去取出这个字段 if len(fieldName) == 0 { // 在结构体中找不到对应的字符 continue } fileObj := sValue.FieldByName(fieldName) // 对其赋值 fmt.Println(fieldName, 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 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\n", cfg) }
-
实例ini:
# mysql config [mysql] address=10.20.30.40 port=3306 username=root password=rootroot # redis config host =11.22.33.44 port=6379 password=root123 database=0 test=false