package main import ( "fmt" "os" "strconv" "strings" "reflect" ) //定义结构体 type MysqlConfig struct { Address string `ini:"address"` Port int `ini:"port"` Username string `ini:"username"` Password string `ini:"password"` } type RedisConfig struct { Host string `ini:"host"` Port int `ini:"port"` Password string `ini:"password"` Database string `ini:"database"` } type Config struct { MysqlConfig `ini:"mysql"` RedisConfig `ini:"redis"` } func Loadini(fileName string, data interface{}) (err error) { //0.参数的校验,对函数中修改值,必须是结构体指针 t := reflect.TypeOf(data) v := reflect.ValueOf(data) //校验指针类型 if t.Kind() != reflect.Ptr { err = fmt.Errorf("data should be pointer") return } //校验结构体类型,由于是指针,要用elem方法 if t.Elem().Kind() != reflect.Struct { err = fmt.Errorf("data should be struct") return } //1.读取文件 b, err := os.ReadFile(fileName) //读取文件拿到字节类型的数据 if err != nil { return } lineSlice := strings.Split(string(b), "\r\n") //字节类型的数据按照换行符切分 fmt.Printf("%#v\n", lineSlice) var structName string //2.一行一行的读取数据 for idx, line := range lineSlice { //2.1如果是注释,就跳过 line = strings.TrimSpace(line) //校验:跳过空行 if len(line) == 0 { continue } if strings.HasPrefix(line, ";") || strings.HasPrefix(line, "#") { continue } //2.2如果是[开头的,就表示节(section) if strings.HasPrefix(line, "[") { //校验1 首尾的[]都需要存在 if line[0] != '[' || line[len(line)-1] != ']' { err = fmt.Errorf("line:%d syntax error", idx+1) return } //结之间能取到的内容,比如redis或者mysql这个字符串 sectionName := strings.TrimSpace(line[1 : len(line)-1]) //校验2 []之间应该有内容 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 field.Tag.Get("ini") == sectionName { //找到对应的嵌套结构体 structName = field.Name fmt.Printf("找到%s对应的嵌套结构体%s\n", sectionName, structName) } } } else { //2.3如果不是[,就是=分割的键值对 //校验:不能不存在等号或者以等号开头 if strings.Index(line, "=") == -1 || strings.HasPrefix(line, "=") { err = fmt.Errorf("line:%d syntax error", idx+1) return } //1.以等号分割这一行,等号左边是key,等号右边是value //2.根据structName去data里把对应的嵌套结构体取出 subValue := v.Elem().FieldByName(structName) subType := subValue.Type() if subValue.Kind() != reflect.Struct { err = fmt.Errorf("field:%s is not struct", structName) return } //切分line这一行,左边是key 右边是value key := strings.Split(line, "=")[0] value := strings.Split(line, "=")[1] //3.遍历嵌套结构体的每一个字段,判断tag是不是等于key,如果等于key,给这个字段赋值 var fieldName string for i := 0; i < subType.NumField(); i++ { field := subType.Field(i) if field.Tag.Get("ini") == key { fieldName = field.Name } } //根据fieldName取出 这个字段,对其赋值 fieldObj := subValue.FieldByName(fieldName) fmt.Println(fieldName, fieldObj.Type().Kind()) switch fieldObj.Type().Kind() { case reflect.String: fieldObj.SetString(value) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: valueInt, err := strconv.ParseInt(value, 10, 64) if err != nil { err = fmt.Errorf("line:%d value:%s error", idx+1, value) return err } fieldObj.SetInt(valueInt) case reflect.Bool: valueBool, err := strconv.ParseBool(value) if err != nil { err = fmt.Errorf("line:%d value:%s error", idx+1, value) return err } fieldObj.SetBool(valueBool) } } } return } func main() { var cfg Config err := Loadini("./ini_unmarshal/config.ini", &cfg) if err != nil { fmt.Println("load ini error:", err) return } fmt.Printf("%#v", cfg) }