go解析 ini 配置文件
conf.ini
[mysql] address=10.20.30.40 port=3306 username=root password=123456
#redis config [redis] xxxx= host=127.0.0.10 port=22 password=3333 databases=0
test=false
package main
import ( "errors" "fmt" "io/ioutil" "reflect" "strconv" "strings" )
//init 文件配置
type MysqlConfig struct { Address string
ini:"address"
Port intini:"port"
Username stringini:"username"
Password stringini:"password"
}type RediConfig struct { Host string
ini:"host"
Port intini:"port"
Password stringini:"password"
Database intini:"databases"
Test boolini:"test"
}// 整个配置文件的结构体 type Config struct { //int
ini:"mysql"
MysqlConfigini:"mysql"
RediConfigini:"redis"
}func loadIni(filname string, data interface{}) (err error) {
//0; 参数的校验 //0.1 传进来的参数必须是指针类型,(因为需要在函数中对其赋值) // 任意值通过reflect.TypeOf()获得反射对象信息后,如果它的类型是结构体, //可以通过反射值对象(reflect.Type)的NumField()和Field()方法获得结构体成员的详细信息。 t := reflect.TypeOf(data) fmt.Println(t, t.Kind()) fmt.Println("---------------------") 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 .") return } //1.读取文件得到字节类型数据 b, err := ioutil.ReadFile(filname) // 把文件读取 出来然后关闭了 if err != nil { err = errors.New("read file error .") return } // 把文件内容 转化为字符串 lineSlice := strings.Split(string(b), "\r\n") fmt.Println("%#v\n", lineSlice) //2.一行一行的读取数据 var structName string for index, line := range lineSlice { // 去掉字符串首尾的空格 line = strings.TrimSpace(line) // r如果空行直接 跳过 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 has syntax error ", index+1) return } // 把这一行首尾的[] 去掉, 取到中间的内容,把首尾的空格去掉,拿到内容 sectionName := strings.TrimSpace(line[1 : len(line)-1]) if len(sectionName) == 0 { err = fmt.Errorf("line : %d has syntax error ", index+1) return } // 根据字符串sectionName 去data 里面根据反射找到对应的结构体 for i := 0; i < t.Elem().NumField(); i++ { field := t.Elem().Field(i) if sectionName == field.Tag.Get("ini") { // 找到 配置文件中的mysql 跟结构体对应 // 找到了对应的结构体, 切片名字记下来 structName = field.Name fmt.Println("找到 %s 对应的嵌套结构体 %s ", sectionName, structName) break } } } else { //2.3 读取每一行 如果不是[]开头的就是 = 分割的键值对 // 1. 以等号分割这一行, 等号左边是key, 等号右边是value if strings.Index(line, "=") == -1 || strings.HasPrefix(line, "=") { err = fmt.Errorf("line: %d syntax error", index+1) return } index := strings.Index(line, "=") key := strings.TrimSpace(line[:index]) value := strings.TrimSpace(line[index+1:]) // 2. 根据structName 去data 里面把对应的嵌套结构体给取出来 v := reflect.ValueOf(data) sValue := v.Elem().FieldByName(structName) // 拿到嵌套结构体的值信息 sType := sValue.Type() // 拿到嵌套结构体的类型信息 structObj := v.Elem().FieldByName(structName) if structObj.Kind() != reflect.Struct { fmt.Println(" data 中的%s 字段应该是个结构体", structName) } var fieldName string var fileType reflect.StructField // 3. 遍历 嵌套结构体的每一个字段, 判断 tag 是不是等于key for i := 0; i < sValue.NumField(); i++ { filed := sType.Field(i) // tag 信息存储在类型信息中 fileType = filed if filed.Tag.Get("ini") == key { // 找到对应的字段 fieldName = filed.Name break } } // 4. 如果key = tag ,给这个字段赋值。 //4.1 根据 fieldName 去取出这个字段 if len(fieldName) == 0 { //如果是 xxx= 就跳过 continue } fileObj := sValue.FieldByName(fieldName) // 4.2 对其赋值 fmt.Println(fieldName, fileType.Type.Name()) 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 erro", index+1) return err } fileObj.SetInt(valueInt) case reflect.Bool: // 如果是 bool 类型的 ,这里我们没用到哦,可以作为扩展 var valueBool bool valueBool, err = strconv.ParseBool(value) if err != nil { err = fmt.Errorf("line: %d value type erro", index+1) return err } fileObj.SetBool(valueBool) } } } return
}
func testini() {
var cfg Config iniFilePath := "C:\\Users\\chaoren\\GolandProjects\\mylogger\\demo\\inidemo\\conf.ini" //1. 加载配置文件 err := loadIni(iniFilePath, &cfg) if err != nil { fmt.Printf("load ini failed , err: %v\n", err) return } fmt.Printf("%#v \n", cfg) //2. 把配置文件映射成 MysqlConfig
}
func main() { testini()
}