Go基础学习-使用gopkg.in/ini.v1实现文件解析及手动实现ini文件解析

gopkg.in/ini.v1

//配置文件
# possible values : production, development
app_mode = development

[paths]
# Path to where grafana can store temp files, sessions, and the sqlite3 db (if that is used)
data = /home/git/grafana

[server]
# Protocol (http or https)
protocol = http

# The http port  to use
http_port = 9999

# Redirect to correct domain if host header does not match domain
# Prevents DNS rebinding attacks
enforce_domain = true
package main

import (
    "fmt"
    "os"

    "gopkg.in/ini.v1"
)

func main() {
    cfg, err := ini.Load("my.ini")
    if err != nil {
        fmt.Printf("Fail to read file: %v", err)
        os.Exit(1)
    }

    // 典型读取操作,默认分区可以使用空字符串表示
    fmt.Println("App Mode:", cfg.Section("").Key("app_mode").String())
    fmt.Println("Data Path:", cfg.Section("paths").Key("data").String())

    // 我们可以做一些候选值限制的操作
    fmt.Println("Server Protocol:",
        cfg.Section("server").Key("protocol").In("http", []string{"http", "https"}))
    // 如果读取的值不在候选列表内,则会回退使用提供的默认值
    fmt.Println("Email Protocol:",
        cfg.Section("server").Key("protocol").In("smtp", []string{"imap", "smtp"}))

    // 试一试自动类型转换
    fmt.Printf("Port Number: (%[1]T) %[1]d\n", cfg.Section("server").Key("http_port").MustInt(9999))
    fmt.Printf("Enforce Domain: (%[1]T) %[1]v\n", cfg.Section("server").Key("enforce_domain").MustBool(false))
    
    // 差不多了,修改某个值然后进行保存
    cfg.Section("").Key("app_mode").SetValue("production")
    cfg.SaveTo("my.ini.local")
}


手动实现ini文件解析

package main

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

//MysqlConfig  mysql配置文件结构体
type MysqlConfig struct {
	Address  string `ini:"address"`
	Port     int64  `ini:"port"`
	Username string `ini:"username"`
	Password string `ini:"password"`
}

//RedisConfig  redis配置文件结构体
type RedisConfig struct {
	Host     string `ini:"host"`
	Port     int32  `ini:"port"`
	Password string `ini:"password"`
	Database string `ini:"database"`
	Test     bool   `ini:"test"`
}

//IniConfig 所有配置文件的结构体集合
type IniConfig struct {
	MysqlConfig `ini:"mysql"`
	RedisConfig `ini:"redis"`
}

//将ini配置文件中的每个小节给写到对应的结构体
//1.验证参数
//1.1是否是指针类型(因为需要在函数中对其赋值)
//1.2是否是结构体指针
//2.读取配置文件
//2.1读取配置文件得到字节类型数据并赋值给一个变量,
//2.2转换为字符串,并以空格来分割字符串
//2.3跳过注释(注释是以;和#开头的)以及空行
//2.4以[来找到小节的头;以此进入每个小节中循环处理
//2.5如果不是以[开头的话,就进入到小节中的内容中拿到键值对
func loadIni(fileName string, v interface{}) (err error) {
	//1
	t := reflect.TypeOf(v)
	//1.1
	if t.Kind() != reflect.Ptr {
		err = fmt.Errorf("params error;must be a pointer")
		return
	}
	//1.2
	if t.Elem().Kind() != reflect.Struct {
		err = fmt.Errorf("params error;must be a struct")
		return
	}
	//2.1
	b, err := ioutil.ReadFile(fileName) //不要使用os.Open等方法;直接使用ioutil读取文件到变量;避免了一致开启着配置文件
	if err != nil {
		return
	}
	//2.2
	sli := strings.Split(string(b), "\r\n")
	// fmt.Printf("%#v\n", sli)
	var configName string
	for lineIndex, sliceEle := range sli {
		//2.3
		sliceEle = strings.TrimSpace(sliceEle) //去除每个元素的首尾空格
		if strings.HasPrefix(sliceEle, ";") || strings.HasPrefix(sliceEle, "#") {
			continue
		}
		if len(sliceEle) == 0 {
			continue
		}

		//2.4
		if strings.HasPrefix(sliceEle, "[") {
			if sliceEle[0] != '[' || sliceEle[len(sliceEle)-1] != ']' {
				err = fmt.Errorf("line:%d;syntax error", lineIndex+1)
				return
			}
			//把这一行首尾的[]去掉,取消空格看剩下的长度并拿到内容
			sectionName := strings.TrimSpace(sliceEle[1 : len(sliceEle)-1])
			if len(sectionName) == 0 {
				err = fmt.Errorf("line:%d;syntax error", lineIndex+1)
				return
			}
			for i := 0; i < reflect.TypeOf(v).Elem().NumField(); i++ { //循环IniConfig结构体取出每个field名
				if reflect.TypeOf(v).Elem().Field(i).Tag.Get("ini") == sectionName {
					configName = reflect.TypeOf(v).Elem().Field(i).Name
				}
			}
		} else {
			//2.5
			if strings.Index(sliceEle, "=") == -1 {
				err = fmt.Errorf("line:%d;syntax error", lineIndex+1)
				return
			}
			if strings.HasPrefix(sliceEle, "=") {
				err = fmt.Errorf("line:%d;syntax error", lineIndex+1)
				return
			}

			setionField := strings.Split(sliceEle, "=")
			eleKey := strings.TrimSpace(setionField[0])
			eleValue := strings.TrimSpace(setionField[1])
			var configName2 string
			var configType2 reflect.StructField
			configValue := reflect.ValueOf(v).Elem().FieldByName(configName) //根据field名拿到对应结构体的Value和Type
			configType := configValue.Type()
			for j := 0; j < configValue.NumField(); j++ {
				field := configType.Field(j)
				configType2 = field
				if field.Tag.Get("ini") == eleKey {
					configName2 = field.Name
					break
				}
			}
			if len(configName2) == 0 { //跳过没有匹配的key
				continue
			}
			fileValue := configValue.FieldByName(configName2)
			switch configType2.Type.Kind() {
			case reflect.String:
				fileValue.SetString(eleValue)
			case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
				var eleInt int64
				eleInt, err = strconv.ParseInt(eleValue, 10, 64)
				if err != nil {
					err = fmt.Errorf("line:%d;syntax error", lineIndex+1)
					return
				}
				fileValue.SetInt(eleInt)
			case reflect.Bool:
				var eleBool bool
				eleBool, err = strconv.ParseBool(eleValue)
				if err != nil {
					err = fmt.Errorf("line:%d;syntax error", lineIndex+1)
					return
				}
				fileValue.SetBool(eleBool)
			case reflect.Float32, reflect.Float64:
				var eleFlo float64
				eleFlo, err = strconv.ParseFloat(eleValue, 64)
				if err != nil {
					err = fmt.Errorf("line:%d;syntax error", lineIndex+1)
					return
				}
				fileValue.SetFloat(eleFlo)
			}
		}
	}
	return
}

func main() {
	var iniC IniConfig
	err := loadIni("./demo.ini", &iniC)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(iniC)
}



猜你喜欢

转载自blog.csdn.net/wzb_wzt/article/details/107419806