golang 使用reflect包实现ini文件解析功能

golang 使用reflect包实现ini文件解析功能

config.ini

[mysqld]
user = mysql
password = abc123
ip = 10.0.0.1
port = 3306

main.go

package main

import (
	"errors"
	"fmt"
	"io/ioutil"
	"reflect"
	"strconv"
	"strings"
)

// 编写代码利用反射实现一个ini文件的解析器程序。
// 根据ini文件的key,输出key对应的value

type MysqlConfig struct {
	Ip       string `ini:"ip"`
	User     string `ini:"user"`
	Password string `ini:"password"`
	Port     int    `ini:"port"`
}

type Config struct {
	MysqlConfig `ini:"mysqld"`
}

func loadIni(filename string, data interface{}) (err error) {
	// 1. 参数校验,参数必须是指针类型和结构体类型
	t := reflect.TypeOf(data)
	if t.Kind() != reflect.Ptr && t.Kind() != reflect.Struct {
		err = errors.New("type error should be struct ptr ")
		return err
	}
	// 2. 打开文件
	file, err := ioutil.ReadFile(filename)
	if err != nil {
		err = fmt.Errorf("open file error %v\n", err)
		return err
	}
	// 3. 一行一行读取文件
	lineSlice := strings.Split(string(file), "\r\n")
	// 定义section切片
	var section string
	var structName string
	// 遍历slice
	for idx, line := range lineSlice {
		// 去除首尾空格
		line = strings.TrimSpace(line)
		// 如果是空行,则继续
		if len(line) == 0 {
			continue
		}
		// 3.1 如果是注释,则跳过
		if strings.HasPrefix(line, ";") || strings.HasPrefix(line, "#") {
			continue
		}
		if strings.HasPrefix(line, "[") {
			// 3.2 如果开头是[,而且是以]结尾的,且[]不为空,则被认定是section
			if line[0] == '[' && line[len(line)-1] == ']' && len(line) > 2 {
				// 将section 加入到section切片变量里
				section = line[1 : len(line)-1]
			}
			//根据字符串section去data里面根据反射找到对应的结构体
			for i := 0; i < t.Elem().NumField(); i++ {
				field := t.Elem().Field(i)
				if section == field.Tag.Get("ini") {
					structName = field.Name
				}
			}
		} else {
			// 4. 拆分键值对
			// 4.1 以等号分割,左边为key,右边为value
			if strings.Index(line, "=") == -1 || strings.HasPrefix(line, "=") {
				err = fmt.Errorf("line:%d syntax error ", idx+1)
				return
			}
			// 获取到line的key和value
			index := strings.Index(line, "=")
			key := strings.TrimSpace(line[:index])     // user
			value := strings.TrimSpace(line[index+1:]) //mysql

			// 4.2 将ini文件中的section和结构体的名字对应
			v := reflect.ValueOf(data)                 // interface的值,此时为空
			sValue := v.Elem().FieldByName(structName) // 结构体的值 { 10.0.0.1 mysql abc123 0}
			sType := sValue.Type()                     // 结构体的类型 main.MysqlConfig

			// 判断结构体的字段是否是struct类型
			if sType.Kind() != reflect.Struct {
				err = fmt.Errorf("should be struct")
				return err
			}
			// 根据structName 去data里面把嵌套的结构体取出
			// 声明字段值
			var fname string                  // 结构体的字段名
			var fieldType reflect.StructField // 结构体字段类型
			for i := 0; i < sValue.NumField(); i++ {
				fieldName := sType.Field(i)
				fieldType = fieldName
				//  遍历结构体的每一个字段,判断这个tag是不是等于key
				if fieldName.Tag.Get("ini") == key {
					// 找到对应的字段
					fname = fieldName.Name
					break
				}
			}
			// 4.3如果key == tag,and value的类型等于结构体定义的类型,则赋值
			fieldObj := sValue.FieldByName(fname)
			switch fieldType.Type.Kind() { // 判断ini文件中value的种类,字串/数字/布尔
			case reflect.String:
				fieldObj.SetString(value)
			case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64:
				var valueInt int64
				valueInt, err := strconv.ParseInt(value, 10, 64)
				if err != nil {
					return err
				}
				fieldObj.SetInt(valueInt)

			case reflect.Bool:
				var valueBool bool
				valueBool, err := strconv.ParseBool(value)
				if err != nil {
					return err
				}
				fieldObj.SetBool(valueBool)
			case reflect.Float32, reflect.Float64:
				var valueFloat float64
				valueFloat, err := strconv.ParseFloat(value, 64)
				if err != nil {
					return err
				}
				fieldObj.SetFloat(valueFloat)
			}
		}
	}
	return
}
func main() {

	var config Config
	err := loadIni("src/github.com/chenjiaona999/home/config.ini", &config)
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(
		config.MysqlConfig.Ip,
		config.MysqlConfig.Password,
		config.MysqlConfig.Port,
		config.MysqlConfig.User,
	)
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值