go语言的ini文件配置读取

项目中我们可能经常要读取配置文件。那么如何读取自己写的配置文件呢?答案是反射。下面举例mysql的配置文件读取的具体操作代码。

1.首先建立myconfig.ini文件,并写入以下内容:
[mysql]
address=localhost
port=3306
username=root
password=root
2.建立对应的结构体
//这个里面的方法和成员变量一定要大写,否则访问不到
type MysqlConfig struct {
	Address  string	`ini:"address"`
	Port     int	`ini:"port"`
	Username string	`ini:"username"`
	Password string	`ini:"password"`
}
//如果有多个配置可以新建多个建构体
3.新建配置类的所有结构体(整合结构体)
type config struct {
	//这个结构体的意义是:例如我们在找到配置文件的[mysql],就知道我应该给mysql的结构体赋值。而不是其他的结构体。通过`ini:"mysql"`能找到我要操作的结构体的名称是MysqlConfig 
	MysqlConfig `ini:"mysql"`
	//这里是为了适应多个配置的情况。暂时就只写一个了
	//RedisConfig `ini:"redis"`
}
4.加载配置类
//这里的data传的应该是&config ,是为了做到多配置的兼容。
func loadIni(fileName string, data interface{}) (err error) {

	t := reflect.TypeOf(data)
	//判断是指针类型
	if t.Kind() != reflect.Ptr {
		err = fmt.Errorf("no ptr")
	}
	//判断必须是结构体指针
	if t.Elem().Kind() !=reflect.Struct{
		err = fmt.Errorf("no struct")
	}

	file, err := ioutil.ReadFile(fileName)
	if err != nil {
		return
	}
	//以行切割
	split := strings.Split(string(file), "\r\n")
	fmt.Println(split)
	var structName string
	for index, line := range split {
		if len(line) == 0{
			continue
		}
		//去掉空格
		line = strings.TrimSpace(line)
		//fmt.Println(data)
		//如果是注释就跳过
		if strings.HasPrefix(line,";") || strings.HasPrefix(line,"#"){
			continue
		}
		//先找要操作的结构体
		if strings.HasPrefix(line,"["){

			//如果是[]这类的就是section
			if line[0] !='[' || line[len(line)-1] != ']'{
				fmt.Errorf("%d line error",index +1 )
				return
			}
			sectioName := strings.TrimSpace(line[1:len(line)-1])
			if len(sectioName) == 0{
				fmt.Errorf("%d line error",index +1 )
				return
			}
			//反射之后遍历config的成员变量,找到要操做的结构体
			for i := 0; i < t.Elem().NumField(); i++ {

				field := t.Elem().Field(i)
				if sectioName == field.Tag.Get("ini"){
					//找到了对应的字段名
					structName = field.Name
					fmt.Printf("找到了%s的结构体,%s\n",sectioName,structName)
					break
				}
			}
		}else {
			//再找键值对
			//如果是键值对,以等号分割去data里面取出结构体
			//遍历嵌套结构体
			if strings.Index(line,"=") == -1 || strings.HasPrefix(line,"=") {
				err = fmt.Errorf("DATA:%s error" ,index +1)
			}

			index := strings.Index(line, "=")
			key :=strings.TrimSpace(line[:index])
			value :=strings.TrimSpace(line[index+1:])
			v := reflect.ValueOf(data)
			fmt.Printf("key ===%s\n",key)
			fmt.Printf("value ===%s\n",value)
			//拿到结构体Value值
			sValue:= v.Elem().FieldByName(structName)
			sType := sValue.Type()
			
			if sType.Kind() != reflect.Struct{
				fmt.Errorf("不是结构体")
			}
			var fileName string
			var fileType reflect.StructField
			//找到结构体的某个字段
			for i := 0; i < sValue.NumField(); i++ {
				filed := sType.Field(i)//tag是存储再类型信息中的
				fileType = filed
				if filed.Tag.Get("ini") == key {
					fileName = filed.Name
					fmt.Printf("fileName ===%s\n",fileName)
					break
				}
			}
			//错误字段,直接跳出。其实应该报错的。
			if len(fileName)== 0{
				continue
			}
			//通过key值找到了某个成员变量的名称,拿到这个成员变量的操作对象
			fileObj := sValue.FieldByName(fileName)
			//根据类型来赋值在配置文件中拿到的value
			switch fileType.Type.Kind() {
			//判断并赋值
			case reflect.String:
				fileObj.SetString(value)
			case reflect.Int,reflect.Int8,reflect.Int16,reflect.Int32,reflect.Int64:
				parseInt, err := strconv.ParseInt(value, 10, 64)
				if err != nil {
					err = fmt.Errorf("转换错误")
				}
				fileObj.SetInt(parseInt)
				//这里还可以添加其他的类型。
			}
		}
	}
	return

}
5.测试。
func main() {
	var mc config
	//传入配置文件的位置。和config结构体的地址
	err := loadIni("./ini/myconfig.ini", &mc)
	if err != nil {
		panic(err)
	}
	fmt.Println("看看反射后的结果:",mc.MysqlConfig)
}

//打印结果: 看看反射后的结果: {localhost 3306 root root}。完美!

总结:通过反射去一步步给需要的结构体的成员变量赋值。在这个过程中有以下几点要注意:
1.成员变量一定要大写开头。不然反射的时候设置值的时候会报错的!这点坑一定要避免。
2.读取配置文件本质是对字符串操作,在其中要多加判断,将不合格的数据直接抛出错误。更加符合现实。
3.逻辑上要清晰,读取过程是以[]为分割,再以键值对给某个结构体的成员变量赋值。声明变量存储某个字段的时候,例如[mysql]这个字段,要存储mysql这个字段,要声明一个是始终在for循环读取的代码的外面,因为下一次可能操作的是redis或其他的配置类。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值