51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

go 语言用反射实现一个ini文件的解析器程序案例

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     int    `ini:"port"`
	Username string `ini:"username"`
	Password string `ini:"password"`
}

type RediConfig struct {
	Host     string `ini:"host"`
	Port     int    `ini:"port"`
	Password string `ini:"password"`
	Database int    `ini:"databases"`
	Test     bool   `ini:"test"`
}

// 整个配置文件的结构体
type Config struct {
	//int `ini:"mysql"`
	MysqlConfig `ini:"mysql"`
	RediConfig  `ini:"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()

}
赞(4)
未经允许不得转载:工具盒子 » go 语言用反射实现一个ini文件的解析器程序案例