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

被折叠的 条评论
为什么被折叠?



