course/ini_unmarshal/conf_ini.go

156 lines
4.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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)
}